/// <summary> /// Finds the configuration info for the name passed in. It will try to /// find a close match if it cannot find an exact macth. /// Return null if no match can be found. /// </summary> private ProjectConfigurationInfo_CPP getConfigurationInfo(string configurationName) { // Have we already worked out which configuration is needed for this name? if (m_mapNamesToConfigurations.ContainsKey(configurationName) == true) { return(m_mapNamesToConfigurations[configurationName]); } // We look for the closest match for the name passed in... ProjectConfigurationInfo_CPP result = null; int closestDistance = -1; foreach (ProjectConfigurationInfo_CPP configurationInfo in m_configurationInfos) { int distance = Utils.levenshteinDistance(configurationName, configurationInfo.Name); if (distance < closestDistance || closestDistance == -1) { result = configurationInfo; closestDistance = distance; } } m_mapNamesToConfigurations.Add(configurationName, result); return(result); }
/// <summary> /// Updates include paths from config settings. /// </summary> private void updateIncludePaths(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); string projectRootFolder = configuration.ParentProjectInfo.RootFolderAbsolute; // We check if any include paths should be removed... List <string> includePaths = new List <string>(configuration.getIncludePaths()); foreach (string includePath in includePaths) { string fullPath = Path.Combine(projectRootFolder, includePath); if (projectSettings.includePathShouldBeRemoved(fullPath) == true) { configuration.removeIncludePath(includePath); } } // We add any new paths... List <string> pathsToAdd = projectSettings.getConfiguration(configuration.Name).getIncludePathsToAdd().ToList(); foreach (string pathToAdd in pathsToAdd) { string relativePath = Utils.makeRelativePath(projectRootFolder, pathToAdd); configuration.addIncludePath(relativePath); } }
/// <summary> /// Updates preprocessor definitions from config settings. /// </summary> private void updatePreprocessorDefinitions(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); // By default we replace WIN32 with GCC_BUILD... configuration.removePreprocessorDefinition("WIN32"); configuration.addPreprocessorDefinition("GCC_BUILD"); // We check if any definitions should be removed... List <string> definitions = new List <string>(configuration.getPreprocessorDefinitions()); foreach (string definition in definitions) { if (projectSettings.preprocessorDefinitionShouldBeRemoved(definition) == true) { configuration.removePreprocessorDefinition(definition); } } // We add any new definitions... List <string> definitionsToAdd = projectSettings.getConfiguration(configuration.Name).getPreprocessorDefinitionsToAdd(); foreach (string definition in definitionsToAdd) { configuration.addPreprocessorDefinition(definition); } }
/// <summary> /// Adds the custom build rule info (for one file) to the configuration /// whose name is passed in. /// </summary> public void addCustomBuildRuleInfo(CustomBuildRuleInfo_CPP ruleInfo, string configurationName) { ProjectConfigurationInfo_CPP configurationInfo = getConfigurationInfo(configurationName); if (configurationInfo != null) { configurationInfo.addCustomBuildRuleInfo(ruleInfo); } }
/// <summary> /// Updates library paths from config settings. /// </summary> private void updateLibraryPaths(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); ProjectInfo projectInfo = configuration.ParentProjectInfo; string projectRootFolder = projectInfo.RootFolderAbsolute; // We check if any library paths should be removed... List <string> libraryPaths = new List <string>(configuration.getLibraryPaths()); foreach (string libraryPath in libraryPaths) { // We remove the library (and re-add it if we need to, but // with the name changed)... configuration.removeLibraryPath(libraryPath); // We find the full path, and add it if we are not // configured to remove it... string fullPath = Path.Combine(projectRootFolder, libraryPath); if (projectSettings.libraryPathShouldBeRemoved(fullPath) == false) { string prefix = MakeItSoConfig.Instance.getProjectConfig(projectInfo.Name).CPPFolderPrefix; string gccPath = Utils.addPrefixToFolderPath(libraryPath, prefix); configuration.addLibraryPath(gccPath); } } // We add any new paths... List <ProjectPath> pathsToAdd = projectSettings.getConfiguration(configuration.Name).getLibraryPathsToAdd(); foreach (ProjectPath pathToAdd in pathsToAdd) { string relativePath; if (!pathToAdd.Absolute) { relativePath = Utils.makeRelativePath(projectRootFolder, pathToAdd.Path); } else { relativePath = pathToAdd.Path; } configuration.addLibraryPath(relativePath); } }
/// <summary> /// Updates compiler flags from the config settings. /// </summary> private void updateCompilerFlags(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); // We check if any definitions should be removed... List<string> flags = new List<string>(configuration.getCompilerFlags()); foreach (string flag in flags) { if (projectSettings.compilerFlagShouldBeRemoved(flag) == true) { configuration.removeCompilerFlag(flag); } } // We add any new definitions... List<string> flagsToAdd = projectSettings.getConfiguration(configuration.Name).getCompilerFlagsToAdd().ToList(); foreach (string flag in flagsToAdd) { configuration.addCompilerFlag(flag); } }
/// <summary> /// Updates compiler flags from the config settings. /// </summary> private void updateCompilerFlags(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); // We check if any definitions should be removed... List <string> flags = new List <string>(configuration.getCompilerFlags()); foreach (string flag in flags) { if (projectSettings.compilerFlagShouldBeRemoved(flag) == true) { configuration.removeCompilerFlag(flag); } } // We add any new definitions... List <string> flagsToAdd = projectSettings.getConfiguration(configuration.Name).getCompilerFlagsToAdd(); foreach (string flag in flagsToAdd) { configuration.addCompilerFlag(flag); } }
/// <summary> /// Updates libraries from config settings. /// </summary> private void updateLibraries(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); // We check if any of the libraries in the configuration should be removed... HashSet <string> libraries = new HashSet <string>(configuration.getLibraryRawNames()); foreach (string library in libraries) { if (projectSettings.libraryShouldBeRemoved(library) == true) { configuration.removeLibraryRawName(library); } } // We add any that need adding... List <string> librariesToAdd = projectSettings.getConfiguration(configuration.Name).getLibrariesToAdd(); foreach (string library in librariesToAdd) { string rawName = Utils.convertLinuxLibraryNameToRawName(library); configuration.addLibraryRawName(rawName); } }
/// <summary> /// We parse the librarian settings, ie link options for libraries. /// </summary> private void parseConfiguration_LibrarianSettings(VCConfiguration vcConfiguration, ProjectConfigurationInfo_CPP configurationInfo) { // We get the librarian 'tool'... IVCCollection tools = Utils.call(() => (vcConfiguration.Tools as IVCCollection)); VCLibrarianTool librarianTool = Utils.call(() => (tools.Item("VCLibrarianTool") as VCLibrarianTool)); if (librarianTool == null) { // Not all projects have a librarian tool... return; } // We find if this library is set to link together other libraries it depends on... // (We are assuming that all configurations of the project have the same link-library-dependencies setting.) m_projectInfo.LinkLibraryDependencies = Utils.call(() => (librarianTool.LinkLibraryDependencies)); parseLibrarianSettings_LibraryPath(vcConfiguration, librarianTool, configurationInfo); parseLibrarianSettings_Libraries(vcConfiguration, librarianTool, configurationInfo); }
/// <summary> /// Parses the configuration (e.g. Debug, Release) passed in. /// </summary> private void parseConfiguration(VCConfiguration vcConfiguration) { ProjectConfigurationInfo_CPP configurationInfo = new ProjectConfigurationInfo_CPP(); configurationInfo.ParentProjectInfo = m_projectInfo; // The configuration name... configurationInfo.Name = Utils.call(() => (vcConfiguration.ConfigurationName)); // The project type. // Note: we are assuming that all the configurations for the project build the // same type of target. m_projectInfo.ProjectType = parseConfiguration_Type(vcConfiguration); // We get the intermediates and output folder, and the Target Name. configurationInfo.IntermediateFolder = parseConfiguration_Folder(vcConfiguration, () => (vcConfiguration.IntermediateDirectory)); configurationInfo.OutputFolder = parseConfiguration_Folder(vcConfiguration, () => (vcConfiguration.OutputDirectory)); configurationInfo.TargetName = parseConfiguration_TargetName(vcConfiguration); // We get compiler settings, such as the include path and // preprocessor definitions... parseConfiguration_CompilerSettings(vcConfiguration, configurationInfo); // We get linker settings, such as any libs to link and the library path... parseConfiguration_LinkerSettings(vcConfiguration, configurationInfo); // We parse librarian settings (how libraries are linked)... parseConfiguration_LibrarianSettings(vcConfiguration, configurationInfo); // We see if there are any pre- or post- build events set up for this // configuration. The only types of events we know how to deal with are // ones that invoke a .cmd file. For these we convert them to invoke // a .sh file with the same name. parseConfiguration_PreBuildEvent(vcConfiguration, configurationInfo); parseConfiguration_PostBuildEvent(vcConfiguration, configurationInfo); // We add the configuration to the collection of them for the project... m_projectInfo.addConfigurationInfo(configurationInfo); }
/// <summary> /// Finds compiler flags. /// </summary> private void parseCompilerSettings_CompilerFlags(VCConfiguration vcConfiguration, VCCLCompilerTool compilerTool, ProjectConfigurationInfo_CPP configurationInfo) { // Warning level... warningLevelOption warningLevel = Utils.call(() => (compilerTool.WarningLevel)); switch (warningLevel) { case warningLevelOption.warningLevel_0: configurationInfo.addCompilerFlag("-w"); break; case warningLevelOption.warningLevel_4: configurationInfo.addCompilerFlag("-Wall"); break; } // Warnings as errors... bool warningsAsErrors = Utils.call(() => (compilerTool.WarnAsError)); if (warningsAsErrors == true) { configurationInfo.addCompilerFlag("-Werror"); } // Optimization... optimizeOption optimization = Utils.call(() => (compilerTool.Optimization)); switch (optimization) { case optimizeOption.optimizeDisabled: configurationInfo.addCompilerFlag("-O0"); break; case optimizeOption.optimizeMinSpace: configurationInfo.addCompilerFlag("-Os"); break; case optimizeOption.optimizeMaxSpeed: configurationInfo.addCompilerFlag("-O2"); break; case optimizeOption.optimizeFull: configurationInfo.addCompilerFlag("-O3"); break; } }
/// <summary> /// Updates library paths from config settings. /// </summary> private void updateLibraryPaths(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); ProjectInfo projectInfo = configuration.ParentProjectInfo; string projectRootFolder = projectInfo.RootFolderAbsolute; // We check if any library paths should be removed... List<string> libraryPaths = new List<string>(configuration.getLibraryPaths()); foreach (string libraryPath in libraryPaths) { // We remove the library (and re-add it if we need to, but // with the name changed)... configuration.removeLibraryPath(libraryPath); // We find the full path, and add it if we are not // configured to remove it... string fullPath = Path.Combine(projectRootFolder, libraryPath); if (projectSettings.libraryPathShouldBeRemoved(fullPath) == false) { string prefix = MakeItSoConfig.Instance.getProjectConfig(projectInfo.Name).CPPFolderPrefix; string gccPath = Utils.addPrefixToFolderPath(libraryPath, prefix); configuration.addLibraryPath(gccPath); } } // We add any new paths... List<string> pathsToAdd = projectSettings.getConfiguration(configuration.Name).getLibraryPathsToAdd().ToList(); foreach (string pathToAdd in pathsToAdd) { string relativePath = Utils.makeRelativePath(projectRootFolder, pathToAdd); configuration.addLibraryPath(relativePath); } }
/// <summary> /// Returns the preprocessor-definitions variable name for the configuration passed in. /// For example "Debug_Preprocessor_Definitions". /// </summary> private string getPreprocessorDefinitionsVariableName(ProjectConfigurationInfo_CPP configuration) { return configuration.Name + "_Preprocessor_Definitions"; }
/// <summary> /// Adds a configuration to the project. /// </summary> public void addConfigurationInfo(ProjectConfigurationInfo_CPP configurationInfo) { m_configurationInfos.Add(configurationInfo); }
/// <summary> /// Parses the configuration (e.g. Debug, Release) passed in. /// </summary> private void parseConfiguration(VCConfiguration vcConfiguration) { ProjectConfigurationInfo_CPP configurationInfo = new ProjectConfigurationInfo_CPP(); configurationInfo.ParentProjectInfo = m_projectInfo; IVCCollection sheets = Utils.call(() => (vcConfiguration.PropertySheets as IVCCollection)); int numSheets = Utils.call(() => (sheets.Count)); for (int i = 1; i <= numSheets; ++i) { VCPropertySheet sheet = Utils.call(() => (sheets.Item(i) as VCPropertySheet)); // 1. The thing is that VCPropertySheet and VCConfiguration have more-or-less // identical interfaces. So we should be able to merge them fairly easily. // // 2. We should try multiple layers of inheritance IVCCollection tools = Utils.call(() => (sheet.Tools as IVCCollection)); VCCLCompilerTool compilerTool = Utils.call(() => (tools.Item("VCCLCompilerTool") as VCCLCompilerTool)); } // The configuration name... configurationInfo.Name = Utils.call(() => (vcConfiguration.ConfigurationName)); // The project type. // Note: we are assuming that all the configurations for the project build the // same type of target. m_projectInfo.ProjectType = parseConfiguration_Type(vcConfiguration); // We get the intermediates folder and output folder... configurationInfo.IntermediateFolder = parseConfiguration_Folder(vcConfiguration, () => (vcConfiguration.IntermediateDirectory)); configurationInfo.OutputFolder = parseConfiguration_Folder(vcConfiguration, () => (vcConfiguration.OutputDirectory)); // We get compiler settings, such as the include path and // preprocessor definitions... parseConfiguration_CompilerSettings(vcConfiguration, configurationInfo); // We get linker settings, such as any libs to link and the library path... parseConfiguration_LinkerSettings(vcConfiguration, configurationInfo); // We parse librarian settings (how libraries are linked)... parseConfiguration_LibrarianSettings(vcConfiguration, configurationInfo); // We see if there are any pre- or post- build events set up for this // configuration. The only types of events we know how to deal with are // ones that invoke a .cmd file. For these we convert them to invoke // a .sh file with the same name. parseConfiguration_PreBuildEvent(vcConfiguration, configurationInfo); parseConfiguration_PostBuildEvent(vcConfiguration, configurationInfo); // We add the configuration to the collection of them for the project... m_projectInfo.addConfigurationInfo(configurationInfo); }
/// <summary> /// Finds compiler settings, such as the include path, for the configuration /// passed in. /// </summary> private void parseConfiguration_CompilerSettings(VCConfiguration vcConfiguration, ProjectConfigurationInfo_CPP configurationInfo) { // We get the compiler-settings 'tool'... IVCCollection tools = Utils.call(() => (vcConfiguration.Tools as IVCCollection)); VCCLCompilerTool compilerTool = Utils.call(() => (tools.Item("VCCLCompilerTool") as VCCLCompilerTool)); // And extract various details from it... parseCompilerSettings_IncludePath(vcConfiguration, compilerTool, configurationInfo); parseCompilerSettings_PreprocessorDefinitions(vcConfiguration, compilerTool, configurationInfo); parseCompilerSettings_CompilerFlags(vcConfiguration, compilerTool, configurationInfo); }
/// <summary> /// We parse the pre-build events. /// </summary> private void parseConfiguration_PreBuildEvent(VCConfiguration vcConfiguration, ProjectConfigurationInfo_CPP configurationInfo) { // We see if there is a pre-build event... IVCCollection tools = Utils.call(() => (vcConfiguration.Tools as IVCCollection)); VCPreBuildEventTool preBuildEvent = Utils.call(() => (tools.Item("VCPreBuildEventTool") as VCPreBuildEventTool)); if (preBuildEvent == null) { return; } string commandLine = Utils.call(() => (preBuildEvent.CommandLine)); configurationInfo.PreBuildEvent = convertBuildEventCommandLine(commandLine); }
/// <summary> /// Finds the library path for the configuration passed in. /// </summary> private void parseLinkerSettings_LibraryPath(VCConfiguration vcConfiguration, VCLinkerTool linkerTool, ProjectConfigurationInfo_CPP configurationInfo) { // We: // 1. Read the additional library paths (which are in a semi-colon-delimited string) // 2. Split it into separate paths // 3. Resolve any symbols // 4. Make sure all paths are relative to the project root folder // 1 & 2... string strAdditionalLibraryDirectories = Utils.call(() => (linkerTool.AdditionalLibraryDirectories)); if (strAdditionalLibraryDirectories == null) { return; } List<string> additionalLibraryDirectories = Utils.split(strAdditionalLibraryDirectories, ';', ','); foreach (string additionalLibraryDirectory in additionalLibraryDirectories) { // The string may be quoted. We need to remove the quotes... string unquotedLibraryDirectory = additionalLibraryDirectory.Trim('"'); if (unquotedLibraryDirectory == "") { continue; } // 3 & 4... string resolvedPath = Utils.call(() => (vcConfiguration.Evaluate(unquotedLibraryDirectory))); if (resolvedPath != "") { string relativePath = Utils.makeRelativePath(m_projectInfo.RootFolderAbsolute, resolvedPath); configurationInfo.addLibraryPath(relativePath); } } }
/// <summary> /// Returns the folder to use for intermediate files. /// </summary> private string getOutputFolder(ProjectConfigurationInfo_CPP configuration) { return Utils.addPrefixToFolderPath(configuration.OutputFolder, m_projectConfig.CPPFolderPrefix); }
/// <summary> /// Returns the library-path variable name for the configuration passed in. /// For example "Debug_Library_Path". /// </summary> private string getLibraryPathVariableName(ProjectConfigurationInfo_CPP configuration) { return configuration.Name + "_Library_Path"; }
/// <summary> /// Sets up implicit linking for this project. /// </summary> public void setupImplicitLinking() { // Implicit linking can be a bit tricky, and works differently // depending on whether the project is an executable or a // library. // Executables // ----------- // 1. We check if the project is set to link dependencies. // If so, we: // 2. Find the libraries that the executable depends and link // them in. // 3. If the libraries themselves depend on other libraries, // we need to link them in as well. We recurse through the // chain of dependencies, to find all the libraries that // the executable depends on. (Subject to rule 4, below.) // 4. If any library is set to link its dependencies, we // link it, but we do not link any of the libraries it // depends on (as they are already linked into the // library itself). // Libraries // --------- // 1. We check if the library is set to link dependencies. // If so, we: // 2. Find the libraries that the project depends on. // We find the list of object files that these libraries // are made up from, and add them to the collection to // create the main library from. // 3. We recurse through any libraries that the libraries // from step 2 depend on. We add their files to this // library as well. // We check if this project should implicitly link the projects // it depends on... if (LinkLibraryDependencies == false) { return; } // We want to link projects we depend on... switch (ProjectType) { case ProjectInfo.ProjectTypeEnum.CPP_EXECUTABLE: { // We find the libraries that this executable depends on... List <ImplicitLinkInfo> infos = new List <ImplicitLinkInfo>(); // Commented as it generates wrong and not needed additional informations. // Is it really needed if project properties are correctly filled? //findImplicitlyLinkedLibraries(infos); // And add them to the library path... foreach (ImplicitLinkInfo info in infos) { // We find the configuration for this info and add // the library to it... ProjectConfigurationInfo_CPP configuration = getConfigurationInfo(info.ConfigurationName); if (configuration == null) { // We should only fail to find a configuration is the // project has no configurations. So really, this should // not happen... Log.log(String.Format("Project {0} could not implicitly link {1}. Could not find the {2} configuration.", Name, info.LibraryRawName, info.ConfigurationName)); continue; } // We add the library and the library path... configuration.addLibraryRawName(info.LibraryRawName); string libraryPath = Utils.makeRelativePath(RootFolderAbsolute, info.OutputFolderAbsolute); configuration.addLibraryPath(libraryPath); } } break; case ProjectInfo_CPP.ProjectTypeEnum.CPP_STATIC_LIBRARY: { // We find the collection of object files used by any // libraries this library depends on... List <ImplicitLinkInfo> infos = new List <ImplicitLinkInfo>(); findImplicitlyLinkedObjectFiles(infos); // We add the files to the configurations... foreach (ImplicitLinkInfo info in infos) { // We find the configuration for this info... ProjectConfigurationInfo_CPP configuration = getConfigurationInfos().Find((cfg) => (cfg.Name == info.ConfigurationName)); // We add the collection of object files to the configuration... string prefix = MakeItSoConfig.Instance.getProjectConfig(Name).CPPFolderPrefix; string intermediateFolderAbsolute = Utils.addPrefixToFolderPath(info.IntermediateFolderAbsolute, prefix); foreach (string objectFile in info.ObjectFileNames) { string absolutePath = intermediateFolderAbsolute + "/" + objectFile; string relativePath = Utils.makeRelativePath(RootFolderAbsolute, absolutePath); configuration.addImplicitlyLinkedObjectFile(relativePath); } } } break; } }
/// <summary> /// Gets the name for the pre-build event target for the configuration /// passed in. /// </summary> private string getPreBuildTargetName(ProjectConfigurationInfo_CPP configurationInfo) { return configurationInfo.Name + "_PreBuildEvent"; }
/// <summary> /// Creates a configuration target. /// </summary> private void createConfigurationTarget(ProjectConfigurationInfo_CPP configurationInfo) { // For example: // // .PHONY: Debug // Debug: debug/main.o debug/math.o debug/utility.o // g++ debug/main.o debug/math.o debug/utility.o -o output/hello.exe // The target name... m_file.WriteLine("# Builds the {0} configuration...", configurationInfo.Name); m_file.WriteLine(".PHONY: {0}", configurationInfo.Name); // The targets that this target depends on... var dependencies = new List<string>(); dependencies.Add("create_folders"); // Is there a pre-build event for this configuration? if (configurationInfo.PreBuildEvent != "") { string preBuildTargetName = getPreBuildTargetName(configurationInfo); dependencies.Add(preBuildTargetName); } // We add any custom build targets as dependencies... foreach (CustomBuildRuleInfo_CPP ruleInfo in configurationInfo.getCustomBuildRuleInfos()) { string ruleTargetName = getCustomRuleTargetName(configurationInfo, ruleInfo); dependencies.Add(ruleTargetName); } // The object files the target depends on... string intermediateFolder = getIntermediateFolder(configurationInfo); var objectFiles = new List<string>(); foreach (string filename in m_projectInfo.getFiles()) { string path = String.Format("{0}/{1}.o", intermediateFolder, Path.GetFileNameWithoutExtension(filename)); objectFiles.Add(path); dependencies.Add(path); } // We write the dependencies... m_file.WriteLine("{0}: {1}", configurationInfo.Name, Utils.join(" ", dependencies)); // We find variables needed for the link step... string outputFolder = getOutputFolder(configurationInfo); // The link step... switch (m_projectInfo.ProjectType) { // Creates a C++ executable... case ProjectInfo_CPP.ProjectTypeEnum.CPP_EXECUTABLE: { m_file.WriteLine("\t$(CPP_COMPILER) {0} $({1}) $({2}) -Wl,-rpath,./ -o {3}/{4}.exe", Utils.join(" ", objectFiles), getLibraryPathVariableName(configurationInfo), getLibrariesVariableName(configurationInfo), outputFolder, m_projectInfo.Name); break; } // Creates a static library... case ProjectInfo_CPP.ProjectTypeEnum.CPP_STATIC_LIBRARY: { m_file.WriteLine("\tar rcs {0}/lib{1}.a {2} $({3})", outputFolder, m_projectInfo.Name, Utils.join(" ", objectFiles), getImplicitlyLinkedObjectsVariableName(configurationInfo)); break; } // Creates a DLL (shared-objects) library... case ProjectInfo_CPP.ProjectTypeEnum.CPP_DLL: { string dllName, pic; if (MakeItSoConfig.Instance.IsCygwinBuild == true) { dllName = String.Format("lib{0}.dll", m_projectInfo.Name); pic = ""; } else { dllName = String.Format("lib{0}.so", m_projectInfo.Name); pic = "-fPIC"; } m_file.WriteLine("\t$(CPP_COMPILER) {0} -shared -Wl,-soname,{1} -o {2}/{1} {3} $({4}) $({5}) $({6})", pic, dllName, outputFolder, Utils.join(" ", objectFiles), getImplicitlyLinkedObjectsVariableName(configurationInfo), getLibraryPathVariableName(configurationInfo), getLibrariesVariableName(configurationInfo)); break; } } // The post-build step, if there is one... if (configurationInfo.PostBuildEvent != "") { m_file.WriteLine("\t" + configurationInfo.PostBuildEvent); } m_file.WriteLine(""); }
/// <summary> /// Updates libraries from config settings. /// </summary> private void updateLibraries(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); // We check if any of the libraries in the configuration should be removed... HashSet<string> libraries = new HashSet<string>(configuration.getLibraryRawNames()); foreach (string library in libraries) { if (projectSettings.libraryShouldBeRemoved(library) == true) { configuration.removeLibraryRawName(library); } } // We add any that need adding... List<string> librariesToAdd = projectSettings.getConfiguration(configuration.Name).getLibrariesToAdd().ToList(); foreach (string library in librariesToAdd) { string rawName = Utils.convertLinuxLibraryNameToRawName(library); configuration.addLibraryRawName(rawName); } }
/// <summary> /// Creates a target for one custom build rule. /// </summary> private void createCustomBuildRuleTarget(ProjectConfigurationInfo_CPP configuration, CustomBuildRuleInfo_CPP ruleInfo) { // The rule might be built by one of the other projects in this solution. // If so, we need to change the folder name to the adjusted output folder // name we generate. (This means that we need to know if the project that // generates it is a C++ or C# project.) string executablePath = Path.Combine(configuration.ParentProjectInfo.RootFolderAbsolute, ruleInfo.RelativePathToExecutable); ProjectInfo.ProjectTypeEnum projectType = m_projectInfo.ParentSolution.isOutputObject(executablePath); string folderPrefix = ""; switch(projectType) { case ProjectInfo.ProjectTypeEnum.CPP_EXECUTABLE: folderPrefix = m_projectConfig.CPPFolderPrefix; break; case ProjectInfo.ProjectTypeEnum.CSHARP_EXECUTABLE: folderPrefix = m_projectConfig.CSharpFolderPrefix; break; } // We add the target to the makefile... m_file.WriteLine("# Custom build rule for " + ruleInfo.RelativePathToFile); string targetName = getCustomRuleTargetName(configuration, ruleInfo); m_file.WriteLine(".PHONY: " + targetName); m_file.WriteLine(targetName + ":"); m_file.WriteLine("\t" + ruleInfo.getCommandLine(folderPrefix)); m_file.WriteLine(""); }
/// <summary> /// Updates preprocessor definitions from config settings. /// </summary> private void updatePreprocessorDefinitions(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); // By default we replace WIN32 with GCC_BUILD... configuration.removePreprocessorDefinition("WIN32"); configuration.addPreprocessorDefinition("GCC_BUILD"); // We check if any definitions should be removed... List<string> definitions = new List<string>(configuration.getPreprocessorDefinitions()); foreach (string definition in definitions) { if(projectSettings.preprocessorDefinitionShouldBeRemoved(definition) == true) { configuration.removePreprocessorDefinition(definition); } } // We add any new definitions... List<string> definitionsToAdd = projectSettings.getConfiguration(configuration.Name).getPreprocessorDefinitionsToAdd().ToList(); foreach (string definition in definitionsToAdd) { configuration.addPreprocessorDefinition(definition); } }
/// <summary> /// We create targets to build any custom build rules associated with /// this configuration. /// </summary> private void createCustomBuildRuleTargets(ProjectConfigurationInfo_CPP configuration) { foreach(CustomBuildRuleInfo_CPP ruleInfo in configuration.getCustomBuildRuleInfos()) { createCustomBuildRuleTarget(configuration, ruleInfo); } }
/// <summary> /// Updates include paths from config settings. /// </summary> private void updateIncludePaths(ProjectConfigurationInfo_CPP configuration) { MakeItSoConfig_Project projectSettings = MakeItSoConfig.Instance.getProjectConfig(configuration.ParentProjectInfo.Name); string projectRootFolder = configuration.ParentProjectInfo.RootFolderAbsolute; // We check if any include paths should be removed... List<string> includePaths = new List<string>(configuration.getIncludePaths()); foreach (string includePath in includePaths) { string fullPath = Path.Combine(projectRootFolder, includePath); if (projectSettings.includePathShouldBeRemoved(fullPath) == true) { configuration.removeIncludePath(includePath); } } // We add any new paths... List<string> pathsToAdd = projectSettings.getConfiguration(configuration.Name).getIncludePathsToAdd().ToList(); foreach (string pathToAdd in pathsToAdd) { string relativePath = Utils.makeRelativePath(projectRootFolder, pathToAdd); configuration.addIncludePath(relativePath); } }
/// <summary> /// Creates targets to compile the files for the configuration passed in. /// </summary> private void createFileTargets(ProjectConfigurationInfo_CPP configurationInfo) { // For example: // // -include debug/main.d // main.o: main.cpp // g++ -c main.cpp [include-path] -o debug/main.o // g++ -MM main.cpp [include-path] > debug/main.d // We find settings that aply to all files in the configuration... string intermediateFolder = getIntermediateFolder(configurationInfo); string includePath = String.Format("$({0})", getIncludePathVariableName(configurationInfo)); string preprocessorDefinitions = String.Format("$({0})", getPreprocessorDefinitionsVariableName(configurationInfo)); string compilerFlags = String.Format("$({0})", getCompilerFlagsVariableName(configurationInfo)); // We write a section of the makefile to compile each file... foreach (string filename in m_projectInfo.getFiles()) { // We work out the filename, the object filename and the // dependencies filename... string path = String.Format("{0}/{1}", intermediateFolder, Path.GetFileName(filename)); string objectPath = Path.ChangeExtension(path, ".o"); string dependenciesPath = Path.ChangeExtension(path, ".d"); // We decide which compiler to use... string compiler = "$(CPP_COMPILER)"; if (Path.GetExtension(filename).ToLower() == ".c") { compiler = "$(C_COMPILER)"; } // We create the target... m_file.WriteLine("# Compiles file {0} for the {1} configuration...", filename, configurationInfo.Name); m_file.WriteLine("-include {0}", dependenciesPath); m_file.WriteLine("{0}: {1}", objectPath, filename); m_file.WriteLine("\t{0} {1} {2} -c {3} {4} -o {5}", compiler, preprocessorDefinitions, compilerFlags, filename, includePath, objectPath); m_file.WriteLine("\t{0} {1} {2} -MM {3} {4} > {5}", compiler, preprocessorDefinitions, compilerFlags, filename, includePath, dependenciesPath); m_file.WriteLine(""); } }
/// <summary> /// Finds the collection of preprocessor definitions for the configuration passed in. /// </summary> private void parseCompilerSettings_PreprocessorDefinitions(VCConfiguration vcConfiguration, VCCLCompilerTool compilerTool, ProjectConfigurationInfo_CPP configurationInfo) { // We read the delimited string of preprocessor definitions, and // split them... string strPreprocessorDefinitions = Utils.call(() => (compilerTool.PreprocessorDefinitions)); if (strPreprocessorDefinitions == null) { return; } List<string> preprocessorDefinitions = Utils.split(strPreprocessorDefinitions, ';'); // We add the definitions to the parsed configuration (removing ones that // aren't relevant to a linux build)... foreach (string definition in preprocessorDefinitions) { configurationInfo.addPreprocessorDefinition(definition); } }
/// <summary> /// Creates a target for the pre-build event, if there is one. /// </summary> private void createPreBuildEventTarget(ProjectConfigurationInfo_CPP configurationInfo) { if (configurationInfo.PreBuildEvent == "") { return; } m_file.WriteLine("# Pre-build step..."); string targetName = getPreBuildTargetName(configurationInfo); m_file.WriteLine(".PHONY: " + targetName); m_file.WriteLine(targetName + ":"); m_file.WriteLine("\t" + configurationInfo.PreBuildEvent); m_file.WriteLine(""); }
/// <summary> /// Finds compiler settings, such as the include path, for the configuration /// passed in. /// </summary> private void parseConfiguration_CompilerSettings(VCConfiguration vcConfiguration, ProjectConfigurationInfo_CPP configurationInfo) { // We get the compiler-settings 'tool'... IVCCollection tools = Utils.call(() => (vcConfiguration.Tools as IVCCollection)); VCCLCompilerTool compilerTool = Utils.call(() => (tools.Item("VCCLCompilerTool") as VCCLCompilerTool)); // And extract various details from it... parseCompilerSettings_IncludePath(vcConfiguration, compilerTool, configurationInfo); parseCompilerSettings_PreprocessorDefinitions(vcConfiguration, compilerTool, configurationInfo); parseCompilerSettings_CompilerFlags(vcConfiguration, compilerTool, configurationInfo); IVCCollection sheets = Utils.call(() => (vcConfiguration.PropertySheets as IVCCollection)); int numSheets = Utils.call(() => (sheets.Count)); for (int i = 1; i <= numSheets; ++i) { VCPropertySheet sheet = Utils.call(() => (sheets.Item(i) as VCPropertySheet)); if (!sheet.IsSystemPropertySheet) { // 1. The thing is that VCPropertySheet and VCConfiguration have more-or-less // identical interfaces. So we should be able to merge them fairly easily. // // 2. We should try multiple layers of inheritance IVCCollection toolsInSheet = Utils.call(() => (sheet.Tools as IVCCollection)); VCCLCompilerTool compilerToolInSheet = Utils.call(() => (toolsInSheet.Item("VCCLCompilerTool") as VCCLCompilerTool)); // And extract various details from it... if (compilerToolInSheet != null) { parseCompilerSettings_IncludePath(vcConfiguration, compilerToolInSheet, configurationInfo); parseCompilerSettings_PreprocessorDefinitions(vcConfiguration, compilerToolInSheet, configurationInfo); //parseCompilerSettings_CompilerFlags(vcConfiguration, compilerToolInSheet, configurationInfo); } } } }
/// <summary> /// Returns the compiler-flags variable name for the configuration passed in. /// For example "Debug_Compiler_Flags". /// </summary> private string getCompilerFlagsVariableName(ProjectConfigurationInfo_CPP configuration) { return configuration.Name + "_Compiler_Flags"; }
/// <summary> /// Finds the linker settings, such as the collection of libraries to link, /// for the configuration passed in. /// </summary> private void parseConfiguration_LinkerSettings(VCConfiguration vcConfiguration, ProjectConfigurationInfo_CPP configurationInfo) { // We get the linker-settings 'tool'... IVCCollection tools = Utils.call(() => (vcConfiguration.Tools as IVCCollection)); VCLinkerTool linkerTool = Utils.call(() => (tools.Item("VCLinkerTool") as VCLinkerTool)); if (linkerTool == null) { // Not all projects have a linker tools... return; } // And extract various details from it... parseLinkerSettings_LibraryPath(vcConfiguration, linkerTool, configurationInfo); parseLinkerSettings_Libraries(vcConfiguration, linkerTool, configurationInfo); parseLinkerSettings_Misc(vcConfiguration, linkerTool, configurationInfo); IVCCollection sheets = Utils.call(() => (vcConfiguration.PropertySheets as IVCCollection)); int numSheets = Utils.call(() => (sheets.Count)); for (int i = 1; i <= numSheets; ++i) { VCPropertySheet sheet = Utils.call(() => (sheets.Item(i) as VCPropertySheet)); if (!sheet.IsSystemPropertySheet) { // 1. The thing is that VCPropertySheet and VCConfiguration have more-or-less // identical interfaces. So we should be able to merge them fairly easily. // // 2. We should try multiple layers of inheritance IVCCollection toolsInSheet = Utils.call(() => (sheet.Tools as IVCCollection)); VCLinkerTool linkerToolInSheet = Utils.call(() => (toolsInSheet.Item("VCLinkerTool") as VCLinkerTool)); // And extract various details from it... if (linkerToolInSheet != null) { parseLinkerSettings_LibraryPath(vcConfiguration, linkerToolInSheet, configurationInfo); parseLinkerSettings_Libraries(vcConfiguration, linkerToolInSheet, configurationInfo); } } } }
/// <summary> /// Gets the target name for the configuration and rule passed in. /// </summary> private string getCustomRuleTargetName(ProjectConfigurationInfo_CPP configurationInfo, CustomBuildRuleInfo_CPP ruleInfo) { // The target-name has this form: // [configuration]_CustomBuildRule_[rule-name]_[file-name] // For example: // Release_CustomBuildRule_Splitter_TextUtils.code string fileName = Path.GetFileName(ruleInfo.RelativePathToFile); return String.Format("{0}_CustomBuildRule_{1}_{2}", configurationInfo.Name, ruleInfo.RuleName, fileName); }
/// <summary> /// Finds the collection of additional libraries to link into this project. /// </summary> private void parseLinkerSettings_Libraries(VCConfiguration vcConfiguration, VCLinkerTool linkerTool, ProjectConfigurationInfo_CPP configurationInfo) { // The collection of libraries is stored in a space-delimited string... string strAdditionalLibraries = Utils.call(() => (linkerTool.AdditionalDependencies)); if (strAdditionalLibraries == null) { return; } List<string> additionalLibraries = Utils.split(strAdditionalLibraries, ' '); foreach (string additionalLibrary in additionalLibraries) { // We add the library to the project... string rawName = Path.GetFileNameWithoutExtension(additionalLibrary); configurationInfo.addLibraryRawName(rawName); } }
/// <summary> /// Returns the implictly-linked-objects variable name for the configuration passed in. /// For example "Debug_Implicitly_Linked_Objects". /// </summary> private string getImplicitlyLinkedObjectsVariableName(ProjectConfigurationInfo_CPP configuration) { return configuration.Name + "_Implicitly_Linked_Objects"; }
/// <summary> /// Reads miscellaneous linker settings. /// </summary> private void parseLinkerSettings_Misc(VCConfiguration vcConfiguration, VCLinkerTool linkerTool, ProjectConfigurationInfo_CPP configurationInfo) { // Whether we implicitly link in libraries we depend on. // (We are assuming that all configurations of the project have the // same link-library-dependencies setting.) m_projectInfo.LinkLibraryDependencies = Utils.call(() => (linkerTool.LinkLibraryDependencies)); // Generate debug info... bool debugInfo = Utils.call(() => (linkerTool.GenerateDebugInformation)); if (debugInfo == true && configurationInfo.getPreprocessorDefinitions().Contains("NDEBUG") == false) { configurationInfo.addCompilerFlag("-g"); } }
/// <summary> /// Returns the include-path variable name for the configuration passed in. /// For example "Debug_Include_Path". /// </summary> private string getIncludePathVariableName(ProjectConfigurationInfo_CPP configuration) { return configuration.Name + "_Include_Path"; }