NMAKE is limited to command-line length of 4096 characters. Due to the
large number of /I directives specified on command line (one per include
directory), the path length of WORKSPACE is multiplied by the number of
/I directives and can exceed the limit.
This patch:
1. Add new build option -l, --cmd-len to set the maximum command line
length, default value is 4096.
2. Generate the response file only if the command line length exceed its
maximum characters (default is 4096) when build the module. Cover
PP_FLAGS, CC_FLAGS, VFRPP_FLAGS, APP_FLAGS, ASLPP_FLAGS, ASLCC_FLAGS and
ASM_FLAGS.
3. The content of the response file is combine from the FLAGS option and
INC option.
4. When build failure, it would print out the response file's file
location and its content.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Yonghong Zhu <yonghong....@intel.com>
---
 BaseTools/Source/Python/AutoGen/AutoGen.py   |   9 +++
 BaseTools/Source/Python/AutoGen/GenMake.py   | 114 ++++++++++++++++++++++++++-
 BaseTools/Source/Python/Common/GlobalData.py |   2 +-
 BaseTools/Source/Python/build/build.py       |  12 +++
 4 files changed, 135 insertions(+), 2 deletions(-)

diff --git a/BaseTools/Source/Python/AutoGen/AutoGen.py 
b/BaseTools/Source/Python/AutoGen/AutoGen.py
index c7aa84f..4934c57 100644
--- a/BaseTools/Source/Python/AutoGen/AutoGen.py
+++ b/BaseTools/Source/Python/AutoGen/AutoGen.py
@@ -2376,10 +2376,11 @@ class ModuleAutoGen(AutoGen):
         self._OutputDir       = None
         self._DebugDir        = None
         self._MakeFileDir     = None
 
         self._IncludePathList = None
+        self._IncludePathLength = 0
         self._AutoGenFileList = None
         self._UnicodeFileList = None
         self._SourceFileList  = None
         self._ObjectFileList  = None
         self._BinaryFileList  = None
@@ -3222,10 +3223,17 @@ class ModuleAutoGen(AutoGen):
                 for Inc in Package.Includes:
                     if Inc not in self._IncludePathList:
                         self._IncludePathList.append(str(Inc))
         return self._IncludePathList
 
+    def _GetIncludePathLength(self):
+        self._IncludePathLength = 0
+        if self._IncludePathList:
+            for inc in self._IncludePathList:
+                self._IncludePathLength += len(' ' + inc)
+        return self._IncludePathLength
+
     ## Get HII EX PCDs which maybe used by VFR
     #
     #  efivarstore used by VFR may relate with HII EX PCDs
     #  Get the variable name and GUID from efivarstore and HII EX PCD
     #  List the HII EX PCDs in As Built INF if both name and GUID match.
@@ -3814,10 +3822,11 @@ class ModuleAutoGen(AutoGen):
     DebugDir        = property(_GetDebugDir)
     MakeFileDir     = property(_GetMakeFileDir)
     CustomMakefile  = property(_GetCustomMakefile)
 
     IncludePathList = property(_GetIncludePathList)
+    IncludePathLength = property(_GetIncludePathLength)
     AutoGenFileList = property(_GetAutoGenFileList)
     UnicodeFileList = property(_GetUnicodeFileList)
     SourceFileList  = property(_GetSourceFileList)
     BinaryFileList  = property(_GetBinaryFiles) # FileType : [File List]
     Targets         = property(_GetTargets)
diff --git a/BaseTools/Source/Python/AutoGen/GenMake.py 
b/BaseTools/Source/Python/AutoGen/GenMake.py
index ec24c70..5543aaa 100644
--- a/BaseTools/Source/Python/AutoGen/GenMake.py
+++ b/BaseTools/Source/Python/AutoGen/GenMake.py
@@ -1,9 +1,9 @@
 ## @file
 # Create makefile for MS nmake and GNU make
 #
-# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
 # This program and the accompanying materials
 # are licensed and made available under the terms and conditions of the BSD 
License
 # which accompanies this distribution.  The full text of the license may be 
found at
 # http://opensource.org/licenses/bsd-license.php
 #
@@ -495,10 +495,26 @@ cleanlib:
                     if Attr == "FLAGS":
                         Value = RemoveDupOption(Value, IncPrefix, 
self._AutoGenObject.IncludePathList)
                     ToolsDef.append("%s_%s = %s" % (Tool, Attr, Value))
             ToolsDef.append("")
 
+        # generate the Response file and Response flag
+        RespDict = self.CommandExceedLimit()
+        RespFileList = os.path.join(self._AutoGenObject.OutputDir, 
'respfilelist.txt')
+        if RespDict:
+            RespFileListContent = ''
+            for Resp in RespDict.keys():
+                RespFile = os.path.join(self._AutoGenObject.OutputDir, 
str(Resp).lower() + '.txt')
+                SaveFileOnChange(RespFile, RespDict[Resp], False)
+                ToolsDef.append("%s = %s" % (Resp, '@' + RespFile))
+                RespFileListContent += '@' + RespFile + os.linesep
+                RespFileListContent += RespDict[Resp] + os.linesep
+            SaveFileOnChange(RespFileList, RespFileListContent, False)
+        else:
+            if os.path.exists(RespFileList):
+                os.remove(RespFileList)
+
         # convert source files and binary files to build targets
         self.ResultFileList = [str(T.Target) for T in 
self._AutoGenObject.CodaTargetList]
         if len(self.ResultFileList) == 0 and 
len(self._AutoGenObject.SourceFileList) <> 0:
             EdkLogger.error("build", AUTOGEN_ERROR, "Nothing to build",
                             ExtraData="[%s]" % str(self._AutoGenObject))
@@ -618,10 +634,106 @@ cleanlib:
             "backward_compatible_target": BcTargetList,
         }
 
         return MakefileTemplateDict
 
+    def CommandExceedLimit(self):
+        FlagDict = {
+                    'CC'    :  { 'Macro' : '$(CC_FLAGS)',    'Value' : False},
+                    'PP'    :  { 'Macro' : '$(PP_FLAGS)',    'Value' : False},
+                    'APP'   :  { 'Macro' : '$(APP_FLAGS)',   'Value' : False},
+                    'ASLPP' :  { 'Macro' : '$(ASLPP_FLAGS)', 'Value' : False},
+                    'VFRPP' :  { 'Macro' : '$(VFRPP_FLAGS)', 'Value' : False},
+                    'ASM'   :  { 'Macro' : '$(ASM_FLAGS)',   'Value' : False},
+                    'ASLCC' :  { 'Macro' : '$(ASLCC_FLAGS)', 'Value' : False},
+                   }
+
+        RespDict = {}
+        FileTypeList = []
+        IncPrefix = self._INC_FLAG_[self._AutoGenObject.ToolChainFamily]
+
+        # base on the source files to decide the file type
+        for File in self._AutoGenObject.SourceFileList:
+            for type in self._AutoGenObject.FileTypes:
+                if File in self._AutoGenObject.FileTypes[type]:
+                    if type not in FileTypeList:
+                        FileTypeList.append(type)
+
+        # calculate the command-line length
+        if FileTypeList:
+            for type in FileTypeList:
+                BuildTargets = 
self._AutoGenObject.BuildRules[type].BuildTargets
+                for Target in BuildTargets:
+                    CommandList = BuildTargets[Target].Commands
+                    for SingleCommand in CommandList:
+                        Tool = ''
+                        SingleCommandLength = len(SingleCommand)
+                        SingleCommandList = SingleCommand.split()
+                        if len(SingleCommandList) > 0:
+                            for Flag in FlagDict.keys():
+                                if '$('+ Flag +')' in SingleCommandList[0]:
+                                    Tool = Flag
+                                    break
+                        if Tool:
+                            SingleCommandLength += 
len(self._AutoGenObject._BuildOption[Tool]['PATH'])
+                            for item in SingleCommandList[1:]:
+                                if FlagDict[Tool]['Macro'] in item:
+                                    Str = 
self._AutoGenObject._BuildOption[Tool]['FLAGS']
+                                    while(Str.find('$(') != -1):
+                                        for macro in 
self._AutoGenObject.Macros.keys():
+                                            MacroName = '$('+ macro + ')'
+                                            if (Str.find(MacroName) != -1):
+                                                Str = Str.replace(MacroName, 
self._AutoGenObject.Macros[macro])
+                                                break
+                                        else:
+                                            EdkLogger.error("build", 
AUTOGEN_ERROR, "Not supported macro is found in make command : %s" % Str, 
ExtraData="[%s]" % str(self._AutoGenObject))
+                                    SingleCommandLength += len(Str)
+                                elif '$(INC)' in item:
+                                    SingleCommandLength += 
self._AutoGenObject.IncludePathLength + len(IncPrefix) * 
len(self._AutoGenObject._IncludePathList)
+                                elif item.find('$(') != -1:
+                                    Str = item
+                                    for Option in 
self._AutoGenObject.BuildOption.keys():
+                                        for Attr in 
self._AutoGenObject.BuildOption[Option]:
+                                            if Str.find(Option + '_' + Attr) 
!= -1:
+                                                Str = Str.replace('$(' + 
Option + '_' + Attr + ')', self._AutoGenObject.BuildOption[Option][Attr])
+                                    while(Str.find('$(') != -1):
+                                        for macro in 
self._AutoGenObject.Macros.keys():
+                                            MacroName = '$('+ macro + ')'
+                                            if (Str.find(MacroName) != -1):
+                                                Str = Str.replace(MacroName, 
self._AutoGenObject.Macros[macro])
+                                                break
+                                        else:
+                                            EdkLogger.error("build", 
AUTOGEN_ERROR, "Not supported macro is found in make command : %s" % Str, 
ExtraData="[%s]" % str(self._AutoGenObject))
+
+                                    SingleCommandLength += len(Str)
+
+                            if SingleCommandLength > 
GlobalData.gCommandMaxLength:
+                                FlagDict[Tool]['Value'] = True
+
+                # generate the response file content by combine the FLAGS and 
INC
+                for Flag in FlagDict.keys():
+                    if FlagDict[Flag]['Value']:
+                        Key = Flag + '_RESP'
+                        RespMacro = FlagDict[Flag]['Macro'].replace('FLAGS', 
'RESP')
+                        Value = self._AutoGenObject.BuildOption[Flag]['FLAGS']
+                        for inc in self._AutoGenObject._IncludePathList:
+                            Value += ' ' + IncPrefix + inc
+                        while (Value.find('$(') != -1):
+                            for macro in self._AutoGenObject.Macros.keys():
+                                MacroName = '$('+ macro + ')'
+                                if (Value.find(MacroName) != -1):
+                                    Value = Value.replace(MacroName, 
self._AutoGenObject.Macros[macro])
+                                    break
+                            else:
+                                EdkLogger.error("build", AUTOGEN_ERROR, "Not 
supported macro is found in make command : %s" % Str, ExtraData="[%s]" % 
str(self._AutoGenObject))
+                        RespDict[Key] = Value
+                        for Target in BuildTargets:
+                            for i, SingleCommand in 
enumerate(BuildTargets[Target].Commands):
+                                if FlagDict[Flag]['Macro'] in SingleCommand:
+                                    BuildTargets[Target].Commands[i] = 
SingleCommand.replace('$(INC)','').replace(FlagDict[Flag]['Macro'], RespMacro)
+        return RespDict
+
     def ProcessBuildTargetList(self):
         #
         # Search dependency file list for each source file
         #
         ForceIncludedFile = []
diff --git a/BaseTools/Source/Python/Common/GlobalData.py 
b/BaseTools/Source/Python/Common/GlobalData.py
index 4234bf5..8f544bd 100644
--- a/BaseTools/Source/Python/Common/GlobalData.py
+++ b/BaseTools/Source/Python/Common/GlobalData.py
@@ -32,11 +32,11 @@ gPlatformPcds = {}
 gPlatformOtherPcds = {}
 gActivePlatform = None
 gCommandLineDefines = {}
 gEdkGlobal = {}
 gOverrideDir = {}
-
+gCommandMaxLength = 4096
 # for debug trace purpose when problem occurs
 gProcessingFile = ''
 gBuildingModule = ''
 
 ## Regular expression for matching macro used in DSC/DEC/INF file inclusion
diff --git a/BaseTools/Source/Python/build/build.py 
b/BaseTools/Source/Python/build/build.py
index 5619638..1607928 100644
--- a/BaseTools/Source/Python/build/build.py
+++ b/BaseTools/Source/Python/build/build.py
@@ -302,10 +302,18 @@ def LaunchCommand(Command, WorkingDir):
 
     # check the return code of the program
     if Proc.returncode != 0:
         if type(Command) != type(""):
             Command = " ".join(Command)
+        # print out the Response file and its content when make failure
+        RespFile = os.path.join(WorkingDir, 'OUTPUT', 'respfilelist.txt')
+        if os.path.isfile(RespFile):
+            f = open(RespFile)
+            RespContent = f.read()
+            f.close()
+            EdkLogger.info(RespContent)
+
         EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % 
(Command, WorkingDir))
 
 ## The smallest unit that can be built in multi-thread build mode
 #
 # This is the base class of build unit. The "Obj" parameter must provide
@@ -773,10 +781,13 @@ class Build():
         self.Platform = None
         self.LoadFixAddress = 0
         self.UniFlag        = BuildOptions.Flag
         self.BuildModules = []
 
+        if BuildOptions.CommandLength:
+            GlobalData.gCommandMaxLength = BuildOptions.CommandLength
+
         # print dot character during doing some time-consuming work
         self.Progress = Utils.Progressor()
 
         self.InitBuild()
 
@@ -1929,10 +1940,11 @@ def MyOptionParser():
     Parser.add_option("-N", "--no-cache", action="store_true", 
dest="DisableCache", default=False, help="Disable build cache mechanism")
     Parser.add_option("--conf", action="store", type="string", 
dest="ConfDirectory", help="Specify the customized Conf directory.")
     Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", 
default=False, help="Check usage content of entries listed in INF file.")
     Parser.add_option("--ignore-sources", action="store_true", 
dest="IgnoreSources", default=False, help="Focus to a binary build and ignore 
all source files")
     Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set 
PCD value by command line. Format: 'PcdName=Value' ")
+    Parser.add_option("-l", "--cmd-len", action="store", type="int", 
dest="CommandLength", help="Specify the maximum line length of build command. 
Default is 4096.")
 
     (Opt, Args) = Parser.parse_args()
     return (Opt, Args)
 
 ## Tool entrance method
-- 
2.6.1.windows.1

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to