# @file NmakeSubdirs.py # This script support parallel build for nmake in windows environment. # It supports Python2.x and Python3.x both. # # Copyright (c) 2018, Intel Corporation. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent # # # Import Modules # from __future__ import print_function import argparse import threading import time import os import subprocess import multiprocessing import copy import sys __prog__ = 'NmakeSubdirs' __version__ = '%s Version %s' % (__prog__, '0.10 ') __copyright__ = 'Copyright (c) 2018, Intel Corporation. All rights reserved.' __description__ = 'Replace for NmakeSubdirs.bat in windows ,support parallel build for nmake.\n' cpu_count = multiprocessing.cpu_count() output_lock = threading.Lock() def RunCommand(WorkDir=None, *Args, **kwargs): if WorkDir is None: WorkDir = os.curdir if "stderr" not in kwargs: kwargs["stderr"] = subprocess.STDOUT if "stdout" not in kwargs: kwargs["stdout"] = subprocess.PIPE p = subprocess.Popen(Args, cwd=WorkDir, stderr=kwargs["stderr"], stdout=kwargs["stdout"]) stdout, stderr = p.communicate() message = "" if stdout is not None: message = stdout.decode(encoding='utf-8', errors='ignore') #for compatibility in python 2 and 3 if p.returncode != 0: raise RuntimeError("Error while execute command \'{0}\' in direcotry {1}\n{2}".format(" ".join(Args), WorkDir, message)) output_lock.acquire(True) print("execute command \"{0}\" in directory {1}".format(" ".join(Args), WorkDir)) print(message) output_lock.release() return p.returncode, stdout class TaskUnit(object): def __init__(self, func, args, kwargs): self.func = func self.args = args self.kwargs = kwargs def __eq__(self, other): return id(self).__eq__(id(other)) def run(self): return self.func(*self.args, **self.kwargs) def __str__(self): para = list(self.args) para.extend("{0}={1}".format(k, v)for k, v in self.kwargs.items()) return "{0}({1})".format(self.func.__name__, ",".join(para)) class ThreadControl(object): def __init__(self, maxthread): self._processNum = maxthread self.pending = [] self.running = [] self.pendingLock = threading.Lock() self.runningLock = threading.Lock() self.error = False self.errorLock = threading.Lock() self.errorMsg = "errorMsg" def addTask(self, func, *args, **kwargs): self.pending.append(TaskUnit(func, args, kwargs)) def waitComplete(self): self._schedule.join() def startSchedule(self): self._schedule = threading.Thread(target=self.Schedule) self._schedule.start() def Schedule(self): for i in range(self._processNum): task = threading.Thread(target=self.startTask) task.daemon = False self.running.append(task) self.runningLock.acquire(True) for thread in self.running: thread.start() self.runningLock.release() while len(self.running) > 0: time.sleep(0.1) if self.error: print("subprocess not exit successfully") print(self.errorMsg) def startTask(self): while True: if self.error: break self.pendingLock.acquire(True) if len(self.pending) == 0: self.pendingLock.release() break task = self.pending.pop(0) self.pendingLock.release() try: task.run() except RuntimeError as e: if self.error: break self.errorLock.acquire(True) self.error = True self.errorMsg = str(e) time.sleep(0.1) self.errorLock.release() break self.runningLock.acquire(True) self.running.remove(threading.currentThread()) self.runningLock.release() def Run(): curdir = os.path.abspath(os.curdir) if len(args.subdirs) == 1: args.jobs = 1 if args.jobs == 1: try: for dir in args.subdirs: RunCommand(os.path.join(curdir, dir), "nmake", args.target, stdout=sys.stdout, stderr=subprocess.STDOUT) except RuntimeError: exit(1) else: controller = ThreadControl(args.jobs) for dir in args.subdirs: controller.addTask(RunCommand, os.path.join(curdir, dir), "nmake", args.target) controller.startSchedule() controller.waitComplete() if controller.error: exit(1) if __name__ == "__main__": parser = argparse.ArgumentParser(prog=__prog__, description=__description__ + __copyright__, conflict_handler='resolve') parser.add_argument("target", help="the target for nmake") parser.add_argument("subdirs", nargs="+", help="the relative dir path of makefile") parser.add_argument("--jobs", type=int, dest="jobs", default=cpu_count, help="thread number") parser.add_argument('--version', action='version', version=__version__) args = parser.parse_args() Run()