private bool make( string configFilePath, // sure this is full path bool isReleaseMode, string gccConfigName, int jobs, string projectNameToBuild, bool isVerbose, string outputFolderPath) { CConsole.writeInfoLine("Config file: " + configFilePath); CConsole.writeInfoLine("Type of build: " + (isReleaseMode ? "release" : "debug")); CConsole.writeInfoLine("GCC config name: " + (string.IsNullOrEmpty(gccConfigName) ? "<default>" : gccConfigName)); CConsole.writeInfoLine("Jobs: " + jobs); CConsole.writeInfoLine("Project to build: " + (string.IsNullOrEmpty(projectNameToBuild) ? "all" : projectNameToBuild)); CConsole.writeInfoLine("Is verbose: " + (isVerbose ? "yes" : "no")); CConsole.writeInfoLine("Out folder: " + (string.IsNullOrEmpty(outputFolderPath) ? "<default>" : outputFolderPath)); CConsole.writeLine(); DateTime totalTimeBegin = DateTime.Now; bool result = true; XmlDocument win2TizXmlDoc; #region Load config file { CConsole.writeInfoLine("Load config file: " + configFilePath); win2TizXmlDoc = new XmlDocument(); try { win2TizXmlDoc.Load(configFilePath); } catch (Exception) { CConsole.writeError("error: could not load config file " + configFilePath + "\n"); result = false; goto my_end; } } #endregion string slnWorkingDir; // sure this is full path ASolution vcSolution; #region Load Visual solution file { slnWorkingDir = Path.GetDirectoryName(configFilePath); XmlNodeList nodes = win2TizXmlDoc.GetElementsByTagName(KXml.s_kSolutionTag); if (nodes == null || nodes.Count <= 0) { CConsole.writeError("error: tag " + KXml.s_kSolutionTag + " is not found\n"); result = false; goto my_end; } XmlAttribute attr = nodes[0].Attributes[KXml.s_kSolutionPathAttr]; if (attr == null) { CConsole.writeError("error: attribute " + KXml.s_kSolutionPathAttr + " of tag " + KXml.s_kSolutionTag + " is not found\n"); result = false; goto my_end; } vcSolution = CFactory.createSolution(); string vcSlnFilePath = PathUtils.combine(slnWorkingDir, attr.Value); CConsole.writeInfoLine("Load Visual solution file: " + vcSlnFilePath); if (!vcSolution.load(vcSlnFilePath)) { CConsole.writeError("error: could not load Visual solution file " + vcSlnFilePath + "\n"); result = false; goto my_end; } } #endregion GccConfig gccConfig; #region Load GccConfig node { XmlNodeList nodes = win2TizXmlDoc.GetElementsByTagName(KXml.s_kCommonGccConfigTag); if (nodes == null || nodes.Count <= 0) { CConsole.writeError("error: tag " + KXml.s_kCommonGccConfigTag + " is not found\n"); result = false; goto my_end; } XmlNode nodeGccConfig = null; foreach (XmlNode node in nodes[0].ChildNodes) { if (node.Name == KXml.s_kGccConfigTag && node.Attributes[KXml.s_kNameAttr] != null && node.Attributes[KXml.s_kNameAttr].Value == gccConfigName) { nodeGccConfig = node; break; } } if (nodeGccConfig == null) { CConsole.writeWarning("warning: this is an old-style config file, the <CommonGccConfig> node will be used as <GccConfig> node\n"); nodeGccConfig = nodes[0]; gccConfigName = null; } gccConfig = new GccConfig(gccConfigName); if (!gccConfig.load(nodeGccConfig, isReleaseMode, slnWorkingDir)) { // TODO: when does it return false? } } CConsole.writeLine(); #endregion string mainProjectName = gccConfig.get_MAIN_PROJECT(); // Can be null // Build only main project? It means buill all if (projectNameToBuild == mainProjectName) { projectNameToBuild = null; } bool onlyBuildOneProject = projectNameToBuild != null && projectNameToBuild != "" && projectNameToBuild != "all"; List <XmlNode> projectNodesToBuild = new List <XmlNode>(); #region Make projectNodesToBuild { XmlNodeList projectNodes = win2TizXmlDoc.GetElementsByTagName(KXml.s_kProjectTag); if (onlyBuildOneProject) { foreach (XmlNode projectNode in projectNodes) { XmlAttribute nameAttr = projectNode.Attributes[KXml.s_kNameAttr]; if (nameAttr == null) { continue; } if (nameAttr.Value == projectNameToBuild) { projectNodesToBuild.Add(projectNode); break; } } if (projectNodesToBuild.Count == 0) { CConsole.writeError("error: project " + projectNameToBuild + " is not found in the config file\n"); result = false; goto my_end; } } else { XmlNode mainProjectNode = null; foreach (XmlNode projectNode in projectNodes) { XmlAttribute nameAttr = projectNode.Attributes[KXml.s_kNameAttr]; if (nameAttr == null) { continue; } if (nameAttr.Value == mainProjectName) { if (mainProjectNode == null) { mainProjectNode = projectNode; } } else { projectNodesToBuild.Add(projectNode); } } // mainProjectNode can be null projectNodesToBuild.Add(mainProjectNode); } } #endregion // lock (m_lock) { m_builder = new Builder(); } int projectCount = projectNodesToBuild.Count; // Sure projectCount >= 1 List <TDepProjectInfo> depProjectInfos = new List <TDepProjectInfo>(); bool isSomethingNewFromDepProjects = false; #region Loop through projectNodesToBuild to get list of commands to execute for (int i = 0; i < projectCount; i++) { XmlNode projectNode = projectNodesToBuild[i]; if (projectNode == null) { continue; } string projectName = projectNode.Attributes[KXml.s_kNameAttr].Value; string projectNameSpec = projectName; CConsole.writeProject(projectName); AProject vcProject = vcSolution.getProject(projectName); if (vcProject == null) { if (onlyBuildOneProject) { CConsole.writeError("error: project " + projectName + " is not found in the Visual solution, it will be ignored\n\n"); result = false; goto my_end; } CConsole.writeWarning("warning: project " + projectName + " is not found in the Visual solution, it will be ignored\n\n"); continue; } #region Process S2G file { XmlAttribute useS2GFileAttr = projectNode.Attributes[KXml.s_kUseS2GFileTag]; if (useS2GFileAttr != null) { XmlDocument s2gDoc = new XmlDocument(); try { s2gDoc.Load(PathUtils.combine(slnWorkingDir, useS2GFileAttr.Value)); XmlNodeList nodes = s2gDoc.GetElementsByTagName(KXml.s_kProjectTag); if (nodes == null || nodes.Count <= 0) { CConsole.writeWarning("warning: could not found tag " + KXml.s_kProjectTag + " in the S2G file " + useS2GFileAttr.Value + " for project " + projectName + ", it will not be used\n"); } else { projectNode = nodes[0]; //=== } } catch (Exception) { CConsole.writeWarning("warning: could not found S2G file " + useS2GFileAttr.Value + " for project " + projectName + ", it will not be used\n"); } } } #endregion bool isMainProject = !onlyBuildOneProject && i == projectCount - 1; EProjectType type = EProjectType.eStaticLib; if (isMainProject) { XmlAttribute tot = projectNode.Attributes["TargetOutType"]; type = (tot != null && tot.Value == "exe") ? EProjectType.eExecutable : EProjectType.eDynamicLib; } List <TCommand> projectCommands = CProject.load( gccConfig, projectNode, vcProject, isReleaseMode, type, depProjectInfos, isSomethingNewFromDepProjects, slnWorkingDir, outputFolderPath, out projectNameSpec); if (projectCommands == null) { CConsole.writeError("error: something went wrong with project " + projectName + ", please double check\n\n"); result = false; goto my_end; } CConsole.writeLine(); if (isSignalToStop()) { result = false; goto my_end; } //---------------------------------------------------------------------------------------- m_builder.addCommands(projectCommands); //---------------------------------------------------------------------------------------- if (!isMainProject) { depProjectInfos.Add(new TDepProjectInfo(projectName, projectNameSpec)); if (projectCommands.Count > 0) { isSomethingNewFromDepProjects = true; } } } #endregion result = m_builder.build(isVerbose, jobs); my_end: m_builder = null; CConsole.writeTime("Total time: " + (DateTime.Now - totalTimeBegin).ToString() + "\n"); return(result); }
public static List <TCommand> load( GccConfig gccConfig, XmlNode projectNode, AProject vcProject, bool isReleaseMode, EProjectType type, List <TDepProjectInfo> depProjectInfos, bool isSomethingNewFromDepProjects, string slnWorkingDir, string outputFolderPath, out string projectNameSpec) { string projectName = vcProject.getName(); string prjWorkingDir; if (string.IsNullOrEmpty(outputFolderPath)) { string cfg = gccConfig.getConfigName(); if (cfg != null) { cfg = cfg.Trim(); } prjWorkingDir = slnWorkingDir + "\\" + (isReleaseMode ? s_kReleaseFolderName : s_kDebugFolderName) + (string.IsNullOrEmpty(cfg) ? "" : "\\" + cfg) + "\\" + projectName; } else { string t = PathUtils.combine(slnWorkingDir, outputFolderPath); prjWorkingDir = PathUtils.combine(t, projectName); } if (!Directory.Exists(prjWorkingDir)) { Directory.CreateDirectory(prjWorkingDir); } string vcDir = vcProject.getDir(); #region Declare info need to get from XML file projectNameSpec = projectName; bool USE_ADDITIONAL_INCLUDE_DIRECTORIES_FROM_VS = true; bool USE_EXCLUDEFROMBUILD_VS_FLAG = false; string msvcConfiguration = null; List <string> ignoredFilters = new List <string>(); List <string> ignoredFilePatterns = new List <string>(); List <TFileSpecific> fileSpecificList = new List <TFileSpecific>(); List <string> addSourceFileToProjectList = new List <string>(); // contains full path int unityBuildsNumber = 0; List <string> excludeFileFromUnityBuild = new List <string>(); string[] INCLUDE_PATHS = null; string[] LINK_PATHS = null; string[] DEFINES = null; string[] LDLIBS = null; string[] LDFLAGS = null; string[] CFLAGS = null; #endregion #region Get info from XML file { foreach (XmlNode childNode in projectNode.ChildNodes) { if (childNode.Name == KXml.s_kMacroTag) { #region Macro XmlAttribute att = childNode.Attributes[KXml.s_kNameAttr]; if (att == null) { continue; } string macroName = att.Value; if (macroName == "USE_SPECIFIC_OUTPUT_NAME") { string val = gccConfig.getValueEscape(CXmlUtils.getXmlValue(childNode, isReleaseMode)).Trim(); if (!string.IsNullOrEmpty(val)) { projectNameSpec = val; } } else if (macroName == "USE_ADDITIONAL_INCLUDE_DIRECTORIES_FROM_VS") { USE_ADDITIONAL_INCLUDE_DIRECTORIES_FROM_VS = StringUtils.convertString2Bool( gccConfig.getValueEscape(CXmlUtils.getXmlValue(childNode, isReleaseMode)) ); } else if (macroName == "USE_EXCLUDEFROMBUILD_VS_FLAG") { USE_EXCLUDEFROMBUILD_VS_FLAG = StringUtils.convertString2Bool( gccConfig.getValueEscape(CXmlUtils.getXmlValue(childNode, isReleaseMode)) ); } else if (macroName == "INCLUDE_PATHS") { INCLUDE_PATHS = StringUtils.splitBySeparate( gccConfig.getValueEscape(CXmlUtils.getXmlValue(childNode, isReleaseMode)) ); for (int i = 0; i < INCLUDE_PATHS.Length; i++) { INCLUDE_PATHS[i] = PathUtils.combine(vcDir, INCLUDE_PATHS[i]); } } else if (macroName == "LINK_PATHS") { LINK_PATHS = StringUtils.splitBySeparate( gccConfig.getValueEscape(CXmlUtils.getXmlValue(childNode, isReleaseMode)) ); for (int i = 0; i < LINK_PATHS.Length; i++) { // TODO: note choice //LINK_PATHS[i] = PathUtils.combine(slnWorkingDir, LINK_PATHS[i]); LINK_PATHS[i] = PathUtils.combine(vcDir, LINK_PATHS[i]); } } else if (macroName == "DEFINES") { DEFINES = StringUtils.splitBySeparate( gccConfig.getValueEscape(CXmlUtils.getXmlValue(childNode, isReleaseMode)) ); } else if (macroName == "LDLIBS") { LDLIBS = StringUtils.splitBySeparate( gccConfig.getValueEscape(CXmlUtils.getXmlValue(childNode, isReleaseMode)) ); } else if (macroName == "LDFLAGS") { LDFLAGS = StringUtils.splitBySeparate( gccConfig.getValueEscape(CXmlUtils.getXmlValue(childNode, isReleaseMode)) ); } else if (macroName == "CFLAGS") { CFLAGS = StringUtils.splitBySeparate( gccConfig.getValueEscape(CXmlUtils.getXmlValue(childNode, isReleaseMode)) ); } #endregion } else if (childNode.Name == KXml.s_kMSVCConfigurationTag) { #region MSVCConfiguration //<MSVCConfiguration Debug="Debug" Release="Release"/> XmlAttribute att = childNode.Attributes[isReleaseMode ? KXml.s_kMSVCConfiguration_ReleaseAttr : KXml.s_kMSVCConfiguration_DebugAttr]; if (att != null) { msvcConfiguration = gccConfig.getValueEscape(att.Value); } #endregion } else if (childNode.Name == KXml.s_kIgnoreTag) { #region Ignore //<Ignore> // <File Name="dummy_main" /> // <Filter Name="win32" /> //</Ignore> foreach (XmlNode iNode in childNode.ChildNodes) { if (iNode.Name == KXml.s_kFileTag) { XmlAttribute att = iNode.Attributes[KXml.s_kNameAttr]; if (att != null) { string fileName = att.Value.Trim(); if (fileName.Length > 0) { ignoredFilePatterns.Add(gccConfig.getValueEscape(fileName)); } } } else if (iNode.Name == KXml.s_kFilterTag) { XmlAttribute att = iNode.Attributes[KXml.s_kNameAttr]; if (att != null) { string filterName = att.Value.Trim(); if (filterName.Length > 0) { ignoredFilters.Add(gccConfig.getValueEscape(filterName)); } } } } #endregion } else if (childNode.Name == KXml.s_kFileSpecificTag) { #region FileSpecific //<FileSpecific> // <File Name="" CFLAGS=""/> // <File Name="" CFLAGS=""/> //</FileSpecific> foreach (XmlNode fileNode in childNode.ChildNodes) { if (fileNode.Name == KXml.s_kFileTag) { XmlAttribute nameAtt = fileNode.Attributes[KXml.s_kNameAttr]; if (nameAtt == null) { continue; } string name = nameAtt.Value.Trim(); if (string.IsNullOrEmpty(name)) { continue; } XmlAttribute cflagsAtt = fileNode.Attributes[KXml.s_kFileSpecific_CFLAGS_Attr]; XmlAttribute definesAtt = fileNode.Attributes[KXml.s_kFileSpecific_DEFINES_Attr]; string cflags = cflagsAtt == null ? null : cflagsAtt.Value.Trim(); string defines = definesAtt == null ? null : definesAtt.Value.Trim(); if (string.IsNullOrEmpty(cflags)) { cflags = null; } if (string.IsNullOrEmpty(defines)) { defines = null; } if (cflags == null && defines == null) { continue; } fileSpecificList.Add(new TFileSpecific( gccConfig.getValueEscape(name), cflags == null ? null : StringUtils.splitBySeparate(gccConfig.getValueEscape(cflags)), defines == null ? null : StringUtils.splitBySeparate(gccConfig.getValueEscape(defines)) )); } } #endregion } else if (childNode.Name == KXml.s_kAddSourceFileToProjectTag) { #region AddSourceFileToProject //<AddSourceFileToProject> // <File Name="$(ANDROID_NDK_PATH)\sources\android\cpufeatures\cpu-features.c"/> // <File Name="$(ANDROID_NDK_PATH)\sources\android\cpufeatures\*.c"/> //</AddSourceFileToProject> foreach (XmlNode iNode in childNode.ChildNodes) { if (iNode.Name == KXml.s_kFileTag) { XmlAttribute nameAtt = iNode.Attributes[KXml.s_kNameAttr]; if (nameAtt == null) { continue; } string name = nameAtt.Value.Trim(); if (string.IsNullOrEmpty(name)) { continue; } name = gccConfig.getValueEscape(name); // Note that name can be a wild card (e.g. *.c), so we cannot call Path.GetFullPath() name = PathUtils.combineSimple(vcDir, name); string[] matchedFiles = PathUtils.getFilesWithWildcard(name); foreach (string f in matchedFiles) { // f can be a non full path (e.g. e:\a\b\c\..\d.cpp addSourceFileToProjectList.Add(Path.GetFullPath(f)); } } } #endregion } else if (childNode.Name == KXml.s_kUnityBuildsTag) { #region UnityBuilds foreach (XmlNode nodeAutoGenerated in childNode.ChildNodes) { if (nodeAutoGenerated.Name != KXml.s_kAutoGeneratedTag) { continue; } XmlAttribute att = nodeAutoGenerated.Attributes[KXml.s_kUnityBuildsNumberAttr]; if (att == null) { continue; } unityBuildsNumber = StringUtils.convertString2Int(att.Value); if (unityBuildsNumber > 0) { foreach (XmlNode nodeExcludeFileFromUnityBuild in nodeAutoGenerated.ChildNodes) { if (nodeExcludeFileFromUnityBuild.Name != KXml.s_kExcludeFileFromUnityBuildTag) { continue; } att = nodeExcludeFileFromUnityBuild.Attributes[KXml.s_kNameAttr]; if (att != null) { string fileName = att.Value.Trim(); if (fileName.Length > 0) { excludeFileFromUnityBuild.Add(fileName); } } } } break; } #endregion } } if (msvcConfiguration == null) { msvcConfiguration = isReleaseMode ? KXml.s_kMSVCConfiguration_ReleaseAttr : KXml.s_kMSVCConfiguration_DebugAttr; } } #endregion List <string> sourceFilePathList = new List <string>(); #region Get source files from VC project { List <string> vcFiles = vcProject.getFiles(msvcConfiguration, ignoredFilters, USE_EXCLUDEFROMBUILD_VS_FLAG); foreach (string sourceFilePath in vcFiles) { string ext = Path.GetExtension(sourceFilePath); if (string.IsNullOrEmpty(ext)) { continue; } if (!gccConfig.isCompileFileType(ext.Substring(1))) { continue; } bool ignored = false; foreach (string pattern in ignoredFilePatterns) { if (PathUtils.checkPatternFile(sourceFilePath, pattern, vcDir)) { ignored = true; break; } } if (ignored) { continue; } if (!PathUtils.checkPathExistInList(sourceFilePath, sourceFilePathList)) { sourceFilePathList.Add(sourceFilePath); } } } #endregion #region addSourceFileToProjectList to sourceFilePathList { foreach (string source in addSourceFileToProjectList) { // Make sure: source is a full path if (!File.Exists(source)) { CConsole.writeWarning("warning: could not found source file " + source + " which is declared in the <AddSourceFileToProjectTag> tag, it will be ignored\n"); continue; } if (!PathUtils.checkPathExistInList(source, sourceFilePathList)) { sourceFilePathList.Add(source); } } } #endregion #region Process UnityBuilds if (unityBuildsNumber > 0 && sourceFilePathList.Count > unityBuildsNumber) { List <string> ubList = new List <string>(); List <string> nmList = new List <string>(); for (int i = 0; i < sourceFilePathList.Count; i++) { bool ub = true; if (!PathUtils.isCppExt(Path.GetExtension(sourceFilePathList[i]))) { ub = false; // this is a C or Assembly file } else { foreach (TFileSpecific fs in fileSpecificList) { if (PathUtils.checkPatternFile(sourceFilePathList[i], fs.name, vcDir)) { ub = false; break; } } if (ub) { foreach (string pattern in excludeFileFromUnityBuild) { if (PathUtils.checkPatternFile(sourceFilePathList[i], pattern, vcDir)) { ub = false; break; } } } } if (ub) { ubList.Add(sourceFilePathList[i]); } else { nmList.Add(sourceFilePathList[i]); } } sourceFilePathList = nmList; // important if (ubList.Count > 0) { int groupSize = ubList.Count / unityBuildsNumber; int extra = ubList.Count % unityBuildsNumber; int left = 0, right = -1; for (int g = 0; g < unityBuildsNumber; g++) { string ubFileName = "UB_" + projectName + "_" + g + ".cpp"; string ubFilePath = prjWorkingDir + "\\" + ubFileName; //int left = g * groupSize; //int right = g < unityBuildsNumber - 1 ? left + groupSize - 1 : ubList.Count - 1; left = right + 1; right = left + groupSize - 1; if (g < extra) { right++; } string stringToWrite = "// [email protected] - This file was auto generated - DO NOT EDIT\r\n"; for (int k = left; k <= right; k++) { //stringToWrite += "#include <" + ubList[k] + ">\r\n"; string rel = PathUtils.getRelativePath(ubList[k], prjWorkingDir); stringToWrite += "#include \"" + rel + "\"\r\n"; } bool writeNew = true; if (File.Exists(ubFilePath)) { using (StreamReader reader = new StreamReader(ubFilePath)) { if (stringToWrite == reader.ReadToEnd()) { writeNew = false; } } } if (writeNew) { using (StreamWriter writer = new StreamWriter(ubFilePath)) { writer.Write(stringToWrite); } } sourceFilePathList.Add(ubFilePath); } } } #endregion string gccDefines = ""; #region DEFINES { if (gccConfig.get_DEFINES() != null) { gccDefines += " " + GccCmdParser.makeGccDefinesString(gccConfig.get_DEFINES()).Trim(); gccDefines = gccDefines.Trim(); } if (DEFINES != null) { gccDefines += " " + GccCmdParser.makeGccDefinesString(DEFINES).Trim(); gccDefines = gccDefines.Trim(); } } #endregion string gccIncludePaths = ""; #region INCLUDE_PATHS { if (INCLUDE_PATHS != null) { gccIncludePaths += " " + GccCmdParser.makeGccIncludePathsString(INCLUDE_PATHS).Trim(); gccIncludePaths = gccIncludePaths.Trim(); } if (USE_ADDITIONAL_INCLUDE_DIRECTORIES_FROM_VS) { List <string> incs = vcProject.getIncludePaths(msvcConfiguration); if (incs != null) { gccIncludePaths += " " + GccCmdParser.makeGccIncludePathsString(incs.ToArray()).Trim(); gccIncludePaths = gccIncludePaths.Trim(); } } if (gccConfig.get_INCLUDE_PATHS() != null) { gccIncludePaths += " " + GccCmdParser.makeGccIncludePathsString(gccConfig.get_INCLUDE_PATHS()).Trim(); gccIncludePaths = gccIncludePaths.Trim(); } } #endregion // TODO: note choice // force to use -g option when GENERATE_DSYM == true //string gccCflags = gccConfig.get_GENERATE_DSYM() ? CUtils.s_kGenDsym : ""; string gccCflags = ""; #region CFLAGS { if (gccConfig.get_CFLAGS() != null) { gccCflags += " " + GccCmdParser.makeGccItemsString(gccConfig.get_CFLAGS()).Trim(); gccCflags = gccCflags.Trim(); } if (CFLAGS != null) { gccCflags += " " + GccCmdParser.makeGccItemsString(CFLAGS).Trim(); gccCflags = gccCflags.Trim(); } } #endregion int sourceCount = sourceFilePathList.Count; bool[] markDuplicate = new bool[sourceCount]; #region markDuplicate { for (int i = 0; i < sourceCount; i++) { markDuplicate[i] = false; } for (int i = 0; i < sourceCount - 1; i++) { if (!markDuplicate[i]) { bool hasDuplicate = false; string f = Path.GetFileNameWithoutExtension(sourceFilePathList[i]); int count = 1; for (int j = i + 1; j < sourceCount; j++) { if (!markDuplicate[j]) { if (f == Path.GetFileNameWithoutExtension(sourceFilePathList[j])) { hasDuplicate = true; markDuplicate[j] = true; count++; } } } if (hasDuplicate) { markDuplicate[i] = true; CConsole.writeWarning("warning: " + count + " source files have the same name " + f + ", they will be always recompiled.\n"); } } } } #endregion List <TCommand> commands = new List <TCommand>(); List <string> objFileNames = new List <string>(); // Used for link command #region Check through sourceFilePathList for dependencies and make compile commands list { CConsole.writeInfoLine("Checking dependencies..."); for (int i = 0; i < sourceCount; i++) { CConsole.write("\r" + (i + 1) + "/" + sourceCount); #region Step string sourceFilePath = sourceFilePathList[i]; string sourceFileNameWithoutExt = Path.GetFileNameWithoutExtension(sourceFilePath); string sourceFileExt = Path.GetExtension(sourceFilePath); bool isAssembly = PathUtils.isAssemblyExt(sourceFileExt); bool recompile = true; if (!markDuplicate[i]) { #region Check LastWriteTime of files and dependencies, output: recompile string basePath = prjWorkingDir + "\\" + sourceFileNameWithoutExt; string objFilePath = basePath + s_kObjFileExt; if (File.Exists(objFilePath)) { DateTime lastWriteTime_ObjFile = File.GetLastWriteTime(objFilePath); if (File.GetLastWriteTime(sourceFilePath) < lastWriteTime_ObjFile) { if (isAssembly) { recompile = false; } else { string depFilePath = basePath + s_kDepFileExt; if (File.Exists(depFilePath)) { #region Check content of the .d file recompile = false; using (StreamReader streamReader = new StreamReader(depFilePath)) { string line; while ((line = streamReader.ReadLine()) != null) { // https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html#Preprocessor-Options //if (!line.EndsWith(":")) continue; //string dFilePath = line.Substring(0, line.Length - 1); //if (File.GetLastWriteTime(dFilePath) >= lastWriteTime_ObjFile) //{ // recompile = true; // break; //} string[] files = StringUtils.splitBySpaceTab(line); foreach (string dFilePath in files) { if (dFilePath[dFilePath.Length - 1] == ':') { continue; } if (dFilePath == "\\") { continue; } if (!File.Exists(dFilePath) || File.GetLastWriteTime(dFilePath) >= lastWriteTime_ObjFile) { //CConsole.writeInfoLine("changed file: " + dFilePath); recompile = true; break; } } if (recompile) { break; } } } #endregion } } } } #endregion } if (!recompile) { objFileNames.Add(sourceFileNameWithoutExt); continue; } TCommand cmd = new TCommand(); cmd.type = ECommandType.eCompile; cmd.prjName = projectName; cmd.workingDir = prjWorkingDir; cmd.alias = Path.GetFileName(sourceFilePath); string compileCommand; #region Make compile command string _gccCflags = gccCflags; string _gccDefines = gccDefines; // FileSpecific foreach (TFileSpecific fs in fileSpecificList) { if (PathUtils.checkPatternFile(sourceFilePath, fs.name, vcDir)) { if (fs.cflags != null) { if (_gccCflags.Length > 0) { _gccCflags += " "; } _gccCflags += GccCmdParser.makeGccItemsString(fs.cflags); _gccCflags = _gccCflags.Trim(); } // if (fs.defines != null) { if (_gccDefines.Length > 0) { _gccDefines += " "; } _gccDefines += GccCmdParser.makeGccDefinesString(fs.defines); _gccDefines = _gccDefines.Trim(); } } } string objFileName = sourceFileNameWithoutExt; // Duplicate objFileName? int count = 0; while (objFileNames.Contains(objFileName)) { objFileName = sourceFileNameWithoutExt + "_" + (++count); } objFileNames.Add(objFileName); objFileName += s_kObjFileExt; string objFilePathFull = PathUtils.combine(prjWorkingDir, objFileName); // Now create compile cmd if (PathUtils.isCppExt(sourceFileExt)) { compileCommand = gccConfig.get_COMPILE_CPP_COMMAND_LINE( _gccDefines, _gccCflags, gccIncludePaths, sourceFilePath, /*objFileName*/ objFilePathFull); } else { compileCommand = gccConfig.get_COMPILE_CC_COMMAND_LINE( _gccDefines, _gccCflags, gccIncludePaths, sourceFilePath, /*objFileName*/ objFilePathFull); } #endregion cmd.command = compileCommand; cmd.verboseString = compileCommand; commands.Add(cmd); #endregion } if (sourceCount > 0) { CConsole.writeLine(); } CConsole.writeInfoLine(commands.Count + " files to be compiled"); } #endregion #region Write the compile commands to file CompileCommands.txt using (StreamWriter writer = new StreamWriter(prjWorkingDir + "\\CompileCommands.txt")) { foreach (TCommand cmd in commands) { writer.WriteLine(cmd.command); } } #endregion int beginLinkCmdIndex = commands.Count; string originalLinkCommand = null; string linkCommand = null; string linkFileName = null; string dsymCommand = null; string dsymFileName = null; string copiedFileName = null; bool copiedCommand = false; string stripCommand = null; string stripFileName = null; #region Make link, DSYM, strip commands string objFilesString = ""; #region Make objFilesString foreach (string objFile in objFileNames) { if (objFilesString.Length > 0) { objFilesString += " "; } objFilesString += objFile + s_kObjFileExt; } #endregion switch (type) { case EProjectType.eStaticLib: { #region Static linkFileName = "lib" + projectNameSpec + ".a"; string linkFilePath = prjWorkingDir + "\\" + linkFileName; bool relink = commands.Count > 0 || !File.Exists(linkFilePath); if (!relink) { // Check .o files // Sure linkFilePath) is existed relink = hasNewerDepFile(prjWorkingDir, objFileNames, s_kObjFileExt, linkFilePath); } if (relink) { originalLinkCommand = gccConfig.get_STATIC_LINK_COMMAND_LINE(linkFileName, objFilesString); int spaceIndex = originalLinkCommand.IndexOf(" "); if (spaceIndex == -1) { CConsole.writeError("error: Invalid link command: " + originalLinkCommand + "\n"); return(null); //=== } string tmpFilePath = Path.GetTempFileName(); using (StreamWriter streamWriter = new StreamWriter(tmpFilePath)) { streamWriter.Write(originalLinkCommand.Substring(spaceIndex + 1)); } linkCommand = originalLinkCommand.Substring(0, spaceIndex) + " @" + tmpFilePath; TCommand cmd = new TCommand( linkCommand, originalLinkCommand, prjWorkingDir, linkFileName, ECommandType.eLinkStatic, projectName ); commands.Add(cmd); } #endregion break; } case EProjectType.eDynamicLib: case EProjectType.eExecutable: { #region Dynamic and executable if (type == EProjectType.eDynamicLib) { string outFileNameBase = "lib" + projectNameSpec; linkFileName = outFileNameBase + ".so.full"; stripFileName = outFileNameBase + ".so"; dsymFileName = outFileNameBase + ".dsym"; copiedFileName = stripFileName; } else { string outFileNameBase = projectNameSpec; linkFileName = outFileNameBase + ".exe.full"; stripFileName = outFileNameBase + ".exe"; dsymFileName = outFileNameBase + ".dsym"; copiedFileName = stripFileName; } string linkFilePath = prjWorkingDir + "\\" + linkFileName; bool relink = isSomethingNewFromDepProjects || commands.Count > 0 || !File.Exists(linkFilePath); if (!relink) { // Check .o files // Sure linkFilePath is existed relink = hasNewerDepFile(prjWorkingDir, objFileNames, s_kObjFileExt, linkFilePath); } if (!relink && depProjectInfos != null && depProjectInfos.Count > 0) { // Check .a files DateTime linkFileDateTime = File.GetLastWriteTime(linkFilePath); foreach (TDepProjectInfo info in depProjectInfos) { string aFilePath = prjWorkingDir + "\\..\\" + info.name + "\\lib" + info.nameSpec + ".a"; // if aFilePath is not existed, we must relink to show an error message if (!File.Exists(aFilePath) || File.GetLastWriteTime(aFilePath) > linkFileDateTime) { relink = true; break; } } } // Make link cmd if (relink) { string gccLdlibs = ""; #region LDLIBS { // TODO: note remove this? //if (depProjectInfos != null && depProjectInfos.Count > 0) //{ // string[] libs = new string[depProjectInfos.Count]; // for (int i = 0; i < libs.Length; i++) // { // libs[i] = depProjectInfos[i].nameSpec; // } // gccLdlibs += " " + CUtils.makeGccLinkLibsString(libs).Trim(); // gccLdlibs = gccLdlibs.Trim(); //} if (LDLIBS != null) { gccLdlibs += " " + GccCmdParser.makeGccItemsString(LDLIBS).Trim(); gccLdlibs = gccLdlibs.Trim(); } if (gccConfig.get_LDLIBS() != null) { gccLdlibs += " " + GccCmdParser.makeGccItemsString(gccConfig.get_LDLIBS()).Trim(); gccLdlibs = gccLdlibs.Trim(); } } #endregion string gccLinkPaths = ""; #region LINK_PATHS { if (depProjectInfos != null && depProjectInfos.Count > 0) { string[] paths = new string[depProjectInfos.Count]; for (int i = 0; i < paths.Length; i++) { paths[i] = "..\\" + depProjectInfos[i].name; } gccLinkPaths += " " + GccCmdParser.makeGccLinkPathsString(paths).Trim(); gccLinkPaths = gccLinkPaths.Trim(); } if (LINK_PATHS != null) { gccLinkPaths += " " + GccCmdParser.makeGccLinkPathsString(LINK_PATHS).Trim(); gccLinkPaths = gccLinkPaths.Trim(); } if (gccConfig.get_LINK_PATHS() != null) { gccLinkPaths += " " + GccCmdParser.makeGccLinkPathsString(gccConfig.get_LINK_PATHS()).Trim(); gccLinkPaths = gccLinkPaths.Trim(); } } #endregion string gccLdflags = ""; #region LDGFLAS if (gccConfig.get_LDFLAGS() != null) { gccLdflags += " " + GccCmdParser.makeGccItemsString(gccConfig.get_LDFLAGS()).Trim(); gccLdflags = gccLdflags.Trim(); } if (LDFLAGS != null) { gccLdflags += " " + GccCmdParser.makeGccItemsString(LDFLAGS).Trim(); gccLdflags = gccLdflags.Trim(); } #endregion linkCommand = type == EProjectType.eDynamicLib ? gccConfig.get_DYNAMIC_LINK_COMMAND_LINE(linkFileName, objFilesString, gccLdlibs, gccLdflags, gccLinkPaths) : gccConfig.get_EXE_LINK_COMMAND_LINE(objFilesString, gccLdlibs, gccLdflags, gccLinkPaths, linkFileName); originalLinkCommand = linkCommand; } // Make DSYM cmd if (gccConfig.get_GENERATE_DSYM()) { // .dsym depends on .so.full / .exe.full string dsymFilePath = prjWorkingDir + "\\" + dsymFileName; bool reGenDsym = relink; if (!reGenDsym) { // Sure linkFilePath is existed because relink == false reGenDsym = !File.Exists(dsymFilePath) || File.GetLastWriteTime(linkFilePath) > File.GetLastWriteTime(dsymFilePath); } if (reGenDsym) { dsymCommand = gccConfig.get_DSYM_COMMAND_LINE(linkFileName, dsymFileName); } } // Make copy cmd { // .so (copied) depends on .so.full string copiedFilePath = prjWorkingDir + "\\" + copiedFileName; bool reCopy = relink; if (!reCopy) { // Sure linkFilePath is existed because relink == false reCopy = !File.Exists(copiedFilePath) || File.GetLastWriteTime(linkFilePath) > File.GetLastWriteTime(copiedFilePath); } if (reCopy) { copiedCommand = true; } } // Make strip cmd if ((isReleaseMode && gccConfig.get_STRIP_DEBUG_SYMBOLS_FOR_RELEASE()) || (!isReleaseMode && gccConfig.get_STRIP_DEBUG_SYMBOLS_FOR_DEBUG())) { // .so (strip) depends on .so.full string stripFilePath = prjWorkingDir + "\\" + stripFileName; bool reStrip = relink; if (!reStrip) { // Sure linkFilePath is existed because relink == false reStrip = !File.Exists(stripFilePath) || File.GetLastWriteTime(linkFilePath) > File.GetLastWriteTime(stripFilePath); } if (reStrip) { stripCommand = gccConfig.get_STRIP_COMMAND_LINE(stripFileName); } } if (linkCommand != null) { TCommand cmd = new TCommand( linkCommand, originalLinkCommand, prjWorkingDir, linkFileName, ECommandType.eLinkDynamic, projectName ); commands.Add(cmd); } if (dsymCommand != null) { TCommand cmd = new TCommand( dsymCommand, dsymCommand, prjWorkingDir, dsymFileName, ECommandType.eGenerateDsym, projectName ); commands.Add(cmd); } if (copiedCommand) { TCommand cmd = new TCommand( prjWorkingDir + "\\" + linkFileName, prjWorkingDir + "\\" + copiedFileName, prjWorkingDir, linkFileName + " -> " + copiedFileName, // alias ECommandType.eCopy, projectName ); commands.Add(cmd); } if (stripCommand != null) { TCommand cmd = new TCommand( stripCommand, stripCommand, prjWorkingDir, stripFileName, ECommandType.eStrip, projectName ); commands.Add(cmd); } #endregion break; } } #endregion #region Write the link commands to file LinkCommands.txt using (StreamWriter writer = new StreamWriter(prjWorkingDir + "\\LinkCommands.txt")) { for (int i = beginLinkCmdIndex; i < commands.Count; i++) { TCommand cmd = commands[i]; if (cmd.type == ECommandType.eLinkStatic || cmd.type == ECommandType.eLinkDynamic) { writer.WriteLine(cmd.command); } } } #endregion return(commands); }