]> git.proxmox.com Git - mirror_edk2.git/commitdiff
BaseTools: Support multi thread build Basetool on Windows
authorDongao Guo <dongao.guo@intel.com>
Tue, 28 Aug 2018 15:26:56 +0000 (23:26 +0800)
committerLiming Gao <liming.gao@intel.com>
Wed, 12 Sep 2018 05:02:11 +0000 (13:02 +0800)
Add NmakeSubdirs.py to replace NmakeSubdirs.bat in VS Makefile. This script will
invoke nmake in multi thread mode. It can save more than half time of BaseTools
C clean build.
GCC make supports multiple thread in make phase. So, GNUmakefile doesn't need apply
this script.

single task or job=1:
    just single thread and invoke subprocess,subprocess will use
    system.stdout to print output.
multi task:
    thread number is logic cpu count.All subprocess output will pass to
    python script by PIPE and then script print it to system.stdout.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Dongao Guo<dongao.guo@intel.com>
Reviewed-by: Liming Gao <liming.gao@intel.com>
Test-by: Liming Gao <liming.gao@intel.com>
BaseTools/Makefile
BaseTools/Source/C/Makefile
BaseTools/Source/C/Makefiles/NmakeSubdirs.py [new file with mode: 0644]

index 3736d85f5f413a02225a35b9b35278f3f46c52b4..b98cd85cb7264ef23bc8bb715be221f00978d241 100644 (file)
@@ -1,7 +1,7 @@
 ## @file\r
 # Windows makefile for Base Tools project build.\r
 #\r
-# Copyright (c) 2007 - 2017, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
 # This program and the accompanying materials\r
 # are licensed and made available under the terms and conditions of the BSD License\r
 # which accompanies this distribution.  The full text of the license may be found at\r
@@ -20,19 +20,19 @@ SUBDIRS = $(BASE_TOOLS_PATH)\Source\C $(BASE_TOOLS_PATH)\Source\Python
 all: c python\r
 \r
 c :\r
-  @$(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.bat all $(BASE_TOOLS_PATH)\Source\C\r
+  @$(PYTHON_HOME)\python.exe $(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.py  all $(BASE_TOOLS_PATH)\Source\C\r
 \r
 python:\r
-  @$(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.bat all $(BASE_TOOLS_PATH)\Source\Python\r
+  @$(PYTHON_HOME)\python.exe $(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.py  all $(BASE_TOOLS_PATH)\Source\Python\r
 \r
 subdirs: $(SUBDIRS)\r
-  @$(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.bat all $**\r
+  @$(PYTHON_HOME)\python.exe $(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.py  all $**\r
 \r
 .PHONY: clean\r
 clean:\r
-  @$(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.bat clean $(SUBDIRS)\r
+  $(PYTHON_HOME)\python.exe $(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.py clean $(SUBDIRS)\r
 \r
 .PHONY: cleanall\r
 cleanall:\r
-  @$(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.bat cleanall $(SUBDIRS)\r
+  $(PYTHON_HOME)\python.exe $(BASE_TOOLS_PATH)\Source\C\Makefiles\NmakeSubdirs.py  cleanall $(SUBDIRS)\r
 \r
index 542818044dd62a707792e7acefca5877c5d8b4d2..1246d23afd3fa9d42df083e45b721ddecd2936c2 100644 (file)
@@ -1,7 +1,7 @@
 ## @file\r
 # Windows makefile for C tools build.\r
 #\r
-# Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>\r
 # This program and the accompanying materials\r
 # are licensed and made available under the terms and conditions of the BSD License\r
 # which accompanies this distribution.  The full text of the license may be found at\r
@@ -16,7 +16,7 @@ HOST_ARCH = IA32
 \r
 LIBRARIES = Common\r
 APPLICATIONS = \\r
-  BootSectImage \\r
+  VfrCompile \\r
   BrotliCompress \\r
   EfiLdrImage \\r
   EfiRom \\r
@@ -32,7 +32,7 @@ APPLICATIONS = \
   Split \\r
   TianoCompress \\r
   VolInfo \\r
-  VfrCompile \\r
+  BootSectImage \\r
   DevicePath\r
 \r
 all: libs apps install\r
@@ -43,7 +43,7 @@ libs: $(LIBRARIES)
        @echo # Build libraries\r
        @echo ######################\r
        @if not exist $(LIB_PATH) mkdir $(LIB_PATH)\r
-       @Makefiles\NmakeSubdirs.bat all $**\r
+       @$(PYTHON_HOME)\python.exe Makefiles\NmakeSubdirs.py all $**\r
 \r
 apps: $(APPLICATIONS)\r
        @echo.\r
@@ -51,7 +51,7 @@ apps: $(APPLICATIONS)
        @echo # Build executables\r
        @echo ######################\r
        @if not exist $(BIN_PATH) mkdir $(BIN_PATH)\r
-       @Makefiles\NmakeSubdirs.bat all $**\r
+       @$(PYTHON_HOME)\python.exe Makefiles\NmakeSubdirs.py all $**\r
 \r
 install: $(LIB_PATH) $(BIN_PATH)\r
        @echo.\r
@@ -65,11 +65,11 @@ install: $(LIB_PATH) $(BIN_PATH)
 \r
 .PHONY: clean\r
 clean:\r
-  @Makefiles\NmakeSubdirs.bat clean $(LIBRARIES) $(APPLICATIONS)\r
+  @$(PYTHON_HOME)\python.exe Makefiles\NmakeSubdirs.py clean $(LIBRARIES) $(APPLICATIONS)\r
 \r
 .PHONY: cleanall\r
 cleanall:\r
-  @Makefiles\NmakeSubdirs.bat cleanall $(LIBRARIES) $(APPLICATIONS)\r
+  @$(PYTHON_HOME)\python.exe Makefiles\NmakeSubdirs.py cleanall $(LIBRARIES) $(APPLICATIONS)\r
 \r
 !INCLUDE Makefiles\ms.rule\r
 \r
diff --git a/BaseTools/Source/C/Makefiles/NmakeSubdirs.py b/BaseTools/Source/C/Makefiles/NmakeSubdirs.py
new file mode 100644 (file)
index 0000000..29bb5df
--- /dev/null
@@ -0,0 +1,169 @@
+# @file NmakeSubdirs.py\r
+# This script support parallel build for nmake in windows environment.\r
+# It supports Python2.x and Python3.x both.\r
+#\r
+#  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
+#\r
+#  This program and the accompanying materials\r
+#  are licensed and made available under the terms and conditions of the BSD License\r
+#  which accompanies this distribution.  The full text of the license may be found at\r
+#  http://opensource.org/licenses/bsd-license.php\r
+#\r
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+\r
+#\r
+# Import Modules\r
+#\r
+\r
+from __future__ import print_function\r
+import argparse\r
+import threading\r
+import time\r
+import os\r
+import subprocess\r
+import multiprocessing\r
+import copy\r
+import sys\r
+__prog__        = 'NmakeSubdirs'\r
+__version__     = '%s Version %s' % (__prog__, '0.10 ')\r
+__copyright__   = 'Copyright (c) 2018, Intel Corporation. All rights reserved.'\r
+__description__ = 'Replace for NmakeSubdirs.bat in windows ,support parallel build for nmake.\n'\r
+\r
+cpu_count = multiprocessing.cpu_count()\r
+output_lock = threading.Lock()\r
+def RunCommand(WorkDir=None, *Args, **kwargs):\r
+    if WorkDir is None:\r
+        WorkDir = os.curdir\r
+    if "stderr" not in kwargs:\r
+        kwargs["stderr"] = subprocess.STDOUT\r
+    if "stdout" not in kwargs:\r
+        kwargs["stdout"] = subprocess.PIPE\r
+    p = subprocess.Popen(Args, cwd=WorkDir, stderr=kwargs["stderr"], stdout=kwargs["stdout"])\r
+    stdout, stderr = p.communicate()\r
+    message = ""\r
+    if stdout is not None:\r
+        message = stdout.decode() #for compatibility in python 2 and 3\r
+\r
+    if p.returncode != 0:\r
+        raise RuntimeError("Error while execute command \'{0}\' in direcotry {1}\n{2}".format(" ".join(Args), WorkDir, message))\r
+\r
+    output_lock.acquire(True)\r
+    print("execute command \"{0}\" in directory {1}".format(" ".join(Args), WorkDir))\r
+    print(message)\r
+    output_lock.release()\r
+\r
+    return p.returncode, stdout\r
+\r
+class TaskUnit(object):\r
+    def __init__(self, func, args, kwargs):\r
+        self.func = func\r
+        self.args = args\r
+        self.kwargs = kwargs\r
+\r
+    def __eq__(self, other):\r
+        return id(self).__eq__(id(other))\r
+\r
+    def run(self):\r
+        return self.func(*self.args, **self.kwargs)\r
+\r
+    def __str__(self):\r
+        para = list(self.args)\r
+        para.extend("{0}={1}".format(k, v)for k, v in self.kwargs.items())\r
+\r
+        return "{0}({1})".format(self.func.__name__, ",".join(para))\r
+\r
+class ThreadControl(object):\r
+\r
+    def __init__(self, maxthread):\r
+        self._processNum = maxthread\r
+        self.pending = []\r
+        self.running = []\r
+        self.pendingLock = threading.Lock()\r
+        self.runningLock = threading.Lock()\r
+        self.error = False\r
+        self.errorLock = threading.Lock()\r
+        self.errorMsg = "errorMsg"\r
+\r
+    def addTask(self, func, *args, **kwargs):\r
+        self.pending.append(TaskUnit(func, args, kwargs))\r
+\r
+    def waitComplete(self):\r
+        self._schedule.join()\r
+\r
+    def startSchedule(self):\r
+        self._schedule = threading.Thread(target=self.Schedule)\r
+        self._schedule.start()\r
+\r
+    def Schedule(self):\r
+        for i in range(self._processNum):\r
+            task = threading.Thread(target=self.startTask)\r
+            task.daemon = False\r
+            self.running.append(task)\r
+\r
+        self.runningLock.acquire(True)\r
+        for thread in self.running:\r
+            thread.start()\r
+        self.runningLock.release()\r
+\r
+        while len(self.running) > 0:\r
+            time.sleep(0.1)\r
+        if self.error:\r
+            print("subprocess not exit sucessfully")\r
+            print(self.errorMsg)\r
+\r
+    def startTask(self):\r
+        while True:\r
+            if self.error:\r
+                break\r
+            self.pendingLock.acquire(True)\r
+            if len(self.pending) == 0:\r
+                self.pendingLock.release()\r
+                break\r
+            task = self.pending.pop(0)\r
+            self.pendingLock.release()\r
+            try:\r
+                task.run()\r
+            except RuntimeError as e:\r
+                if self.error: break\r
+                self.errorLock.acquire(True)\r
+                self.error = True\r
+                self.errorMsg = str(e)\r
+                time.sleep(0.1)\r
+                self.errorLock.release()\r
+                break\r
+\r
+        self.runningLock.acquire(True)\r
+        self.running.remove(threading.currentThread())\r
+        self.runningLock.release()\r
+\r
+def Run():\r
+    curdir = os.path.abspath(os.curdir)\r
+    if len(args.subdirs) == 1:\r
+        args.jobs = 1\r
+    if args.jobs == 1:\r
+        try:\r
+            for dir in args.subdirs:\r
+                RunCommand(os.path.join(curdir, dir), "nmake", args.target, stdout=sys.stdout, stderr=subprocess.STDOUT)\r
+        except RuntimeError:\r
+            exit(1)\r
+    else:\r
+        controller = ThreadControl(args.jobs)\r
+        for dir in args.subdirs:\r
+            controller.addTask(RunCommand, os.path.join(curdir, dir), "nmake", args.target)\r
+        controller.startSchedule()\r
+        controller.waitComplete()\r
+        if controller.error:\r
+            exit(1)\r
+\r
+if __name__ == "__main__":\r
+    parser = argparse.ArgumentParser(prog=__prog__, description=__description__ + __copyright__, conflict_handler='resolve')\r
+\r
+    parser.add_argument("target", help="the target for nmake")\r
+    parser.add_argument("subdirs", nargs="+", help="the relative dir path of makefile")\r
+    parser.add_argument("--jobs", type=int, dest="jobs", default=cpu_count, help="thread number")\r
+    parser.add_argument('--version', action='version', version=__version__)\r
+    args = parser.parse_args()\r
+    Run()\r
+\r