/// <summary> /// Initializes the container with all values. /// </summary> /// <param name="accessor">Function that returns a value depending on platform/configuration.</param> public CollectorContainer(Func <TargetPlatform, TargetConfiguration, T> accessor) { m_Container = new Dictionary <ushort, T>(); foreach (var platform in TargetInfo.EnumerateAllPlatforms()) { foreach (var configuration in TargetInfo.EnumerateAllConfigurations()) { m_Container.Add(CompressPlatformConfiguration(platform, configuration), accessor(platform, configuration)); } } }
/// <summary> /// Accesses the platform/configuration specific value of the container. /// </summary> /// <param name="platform">One of the target platforms.</param> /// <param name="configuration">One of the target configurations.</param> public T this[TargetPlatform platform, TargetConfiguration configuration] { get { if (platform == TargetPlatform.Any) { platform = TargetInfo.EnumerateAllPlatforms().First(); } if (configuration == TargetConfiguration.Any) { configuration = TargetInfo.EnumerateAllConfigurations().First(); } return(m_Container[CompressPlatformConfiguration(platform, configuration)]); } }
/// <summary> /// Generates solution files for Visual Studio. /// </summary> /// <param name="solution">Parsed solution object.</param> /// <returns>Path to Visual Studio '.sln' file.</returns> public sealed override string GenerateSolutionFiles(Solution solution) { // ========================================================================================== // Generating SLN files.. // ========================================================================================== var slnPath = Path.Combine(base.GenerateSolutionFiles(solution), solution.Name + ".sln"); using (var sln = new StreamWriter(slnPath)) { sln.WriteLine(); sln.WriteLine("Microsoft Visual Studio Solution File, Format Version 12.00"); sln.WriteLine("# Generated by GoddamnBuildSystem. Please, do not edit this file manually."); // ------------------------------------------------------------------------------------------ // Caching GUIDs of projects and filters and defining filters. // ------------------------------------------------------------------------------------------ var solutionFiltersGuidCache = new Dictionary <int, string>(); foreach (var solutionProject in solution.Projects) { // Loading the GUID of a build tool.. if (solutionProject.IsBuildTool) { // For build tools the best was is to load a GUID from project files: this causes exceptions // in Visual Studio when reloading of solution, and all opened tabs would be terminated.. try { solutionProject.Misc.GUID = QueryGuidFromProject(solutionProject.GeneratorOutputPath); } catch (Exception exception) { Console.Error.WriteLine("Unable to load a GUID of C# project \"{0}\". Check this out.", solution.GeneratedSolutionPath); Console.Error.WriteLine(exception.ToString()); solutionProject.Misc.GUID = CreateMsBuildGuid(); } } // Looking for projects with filters which we have not already cached. if (!string.IsNullOrEmpty(solutionProject.Filter)) { var solutionProjectFilterHash = solutionProject.Filter.GetHashCode(); if (!solutionFiltersGuidCache.ContainsKey(solutionProjectFilterHash)) { // Generating and caching filter GUIDS. solutionProject.Misc.FilterGUID = CreateMsBuildGuid(); solutionFiltersGuidCache.Add(solutionProjectFilterHash, solutionProject.Misc.FilterGUID); // E.g. 'Project({Filter-GUID}) = "Name", "Name", "Filter-GUID"' sln.WriteLine("Project(\"{0}\") = \"{1}\", \"{1}\", \"{2}\"", c_SlnFilterProjectGuid, solutionProject.Filter, solutionProject.Misc.FilterGUID); sln.WriteLine("EndProject"); } else { solutionProject.Misc.FilterGUID = solutionFiltersGuidCache[solutionProjectFilterHash]; } } } // ------------------------------------------------------------------------------------------ // Defining Solution projects. // ------------------------------------------------------------------------------------------ foreach (var solutionProject in solution.Projects) { // E.g. 'Project({Type-GUID}) = "Name", "Path-ToProject-File", "Project-GUID"' sln.WriteLine("Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\"", /* 0 */ solutionProject.IsBuildTool ? c_SlnCsProjProjectGuid : c_SlnVcxProjProjectGuid, /*1,2*/ solutionProject.Name, solutionProject.GeneratorOutputPath, /* 3 */ solutionProject.Misc.GUID); // Defining projects this one depends on. sln.WriteLine("\tProjectSection(ProjectDependencies) = postProject"); var project = solutionProject; foreach (var solutionDependentProject in solution.Projects) { if (solutionDependentProject.Priority > project.Priority) { // Writing projects with higher priority as ones we depend from. sln.WriteLine("\t\t{0} = {0}", solutionDependentProject.Misc.GUID); } } sln.WriteLine("\tEndProjectSection"); sln.WriteLine("EndProject"); } sln.WriteLine("Global"); // ------------------------------------------------------------------------------------------ // Linking platforms & configurations. // ------------------------------------------------------------------------------------------ sln.WriteLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); foreach (var platform in TargetInfo.EnumerateAllPlatforms()) { var platformInfo = TargetPlatformInfo.Get(platform); foreach (var configurationInfo in TargetInfo.EnumerateAllConfigurations().Select(TargetConfigurationInfo.Get)) { // E.g. \tConfiguration-Name|Platform-Name = Configuration-Name|Platform-Name sln.WriteLine("\t\t{0}|{1} = {0}|{1}", configurationInfo.HumanReadableName, platformInfo.HumanReadableName); } } sln.WriteLine("\tEndGlobalSection"); sln.WriteLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); foreach (var solutionProject in solution.Projects) { foreach (var platform in TargetInfo.EnumerateAllPlatforms()) { var platformInfo = TargetPlatformInfo.Get(platform); foreach (var configuration in TargetInfo.EnumerateAllConfigurations()) { var configurationInfo = TargetConfigurationInfo.Get(configuration); var platfromConfig = solutionProject.IsBuildTool ? $"\t\t{solutionProject.Misc.GUID}.{configurationInfo.HumanReadableName}|{platformInfo.HumanReadableName}.* = {(configurationInfo.IsDebug ? "Debug" : "Release")}|Any CPU" : $"\t\t{solutionProject.Misc.GUID}.{configurationInfo.HumanReadableName}|{platformInfo.HumanReadableName}.* = {configuration}{platform}|{ConvertPlatformToMsBuildPlatform(platform)}"; sln.WriteLine(platfromConfig.Replace("*", "ActiveCfg")); sln.WriteLine(platfromConfig.Replace("*", "Build.0")); } } } sln.WriteLine("\tEndGlobalSection"); sln.WriteLine("\tGlobalSection(SolutionProperties) = postSolution"); sln.WriteLine("\t\tHideSolutionNode = FALSE"); sln.WriteLine("\tEndGlobalSection"); // ------------------------------------------------------------------------------------------ // Matching projects and filters hierarchy. // ------------------------------------------------------------------------------------------ sln.WriteLine("\tGlobalSection(NestedProjects) = preSolution"); foreach (var solutionProject in solution.Projects) { if (!string.IsNullOrEmpty(solutionProject.Filter)) { sln.WriteLine("\t\t{0} = {1}", solutionProject.Misc.GUID, solutionProject.Misc.FilterGUID); } } sln.WriteLine("\tEndGlobalSection"); sln.WriteLine("EndGlobal"); } return(slnPath); }
/// <summary> /// Generates project files for Visual Studio: '.vcxproj' and '.vcxproj.filter'. /// </summary> /// <param name="project">Parsed project object.</param> /// <returns>Path to Visual Studio '.vcxproj file'.</returns> public sealed override string GenerateProjectFiles(Project project) { var vcxProjPath = Path.Combine(base.GenerateProjectFiles(project), project.Name) + ".vcxproj"; try { // For build tools the best was is to load a GUID from project files: this causes exceptions // in Visual Studio when reloading of solution, and all opened tabs would be terminated. project.Misc.GUID = QueryGuidFromProject(vcxProjPath); } catch (Exception) { project.Misc.GUID = CreateMsBuildGuid(); } // ========================================================================================== // Generating VCXPROJ files. // ========================================================================================== using (var vcxProj = new XmlTextWriter(vcxProjPath, null) { Formatting = Formatting.Indented, Indentation = 1, IndentChar = '\t' }) { vcxProj.WriteStartDocument(); vcxProj.WriteComment("Generated by GoddamnBuildSystem. Please, do not edit this file manually."); vcxProj./**/ WriteStartElement("Project", c_MsBuild2003Namespace); vcxProj./**//**/ WriteAttributeString("ToolsVersion", c_ToolsVersion); vcxProj./**//**/ WriteAttributeString("DefaultTargets", "Build"); // ------------------------------------------------------------------------------------------ // Defining list of configurations. // ------------------------------------------------------------------------------------------ vcxProj.WriteStartElement("ItemGroup"); vcxProj./**/ WriteAttributeString("Label", "ProjectConfigurations"); foreach (var platform in TargetInfo.EnumerateAllPlatforms()) { var platformString = ConvertPlatformToMsBuildPlatform(platform); foreach (var configuration in TargetInfo.EnumerateAllConfigurations()) { var configurationName = string.Concat(configuration, platform); vcxProj./**/ WriteStartElement("ProjectConfiguration"); vcxProj./**//**/ WriteAttributeString("Include", configurationName + '|' + platformString); vcxProj./**//**/ WriteElementString("Configuration", configurationName); vcxProj./**//**/ WriteElementString("Platform", platformString); vcxProj./**/ WriteEndElement(); } } vcxProj.WriteEndElement(); // ------------------------------------------------------------------------------------------ // Defining list of source files. // ------------------------------------------------------------------------------------------ foreach (var platform in TargetInfo.EnumerateAllPlatforms()) { var platformString = ConvertPlatformToMsBuildPlatform(platform); foreach (var configuration in TargetInfo.EnumerateAllConfigurations()) { var configurationName = string.Concat(configuration, platform); vcxProj.WriteStartElement("ItemGroup"); vcxProj.WriteAttributeStringFormat("Condition", @"'$(Configuration)|$(Platform)'=='{0}|{1}'", configurationName, platformString); foreach (var projectSource in project.Files[platform, configuration]) { vcxProj.WriteStartElement(ConvertFileTypeToVcxProjElement(projectSource.FileType)); vcxProj.WriteAttributeString("Include", projectSource.FilePath); vcxProj.WriteEndElement(); } vcxProj.WriteEndElement(); } } // ------------------------------------------------------------------------------------------ // Overriding global project properties. // ------------------------------------------------------------------------------------------ vcxProj.WriteStartElement("PropertyGroup"); vcxProj./**/ WriteElementString("ProjectGuid", project.Misc.GUID); vcxProj./**/ WriteElementString("RootNamespace", project.Name); vcxProj./**/ WriteElementString("MinimumVisualStudioVersion", "14.0"); vcxProj.WriteEndElement(); foreach (var platform in TargetInfo.EnumerateAllPlatforms()) { var platformString = ConvertPlatformToMsBuildPlatform(platform); foreach (var configuration in TargetInfo.EnumerateAllConfigurations()) { var configurationName = string.Concat(configuration, platform); vcxProj.WriteStartElement("PropertyGroup"); vcxProj./**/ WriteAttributeStringFormat("Condition", "'$(Configuration)|$(Platform)'=='{0}|{1}'", configurationName, platformString); vcxProj./**/ WriteAttributeString("Label", "Globals"); vcxProj./**/ WriteElementString("WindowsTargetPlatformVersion", "10.0.16299.0"); if (platform == TargetPlatform.XboxOne) { vcxProj./**/ WriteElementString("AppContainerApplication", "true"); vcxProj./**/ WriteElementString("ApplicationType", "Windows Store"); vcxProj./**/ WriteElementString("WindowsTargetPlatformVersion", "10.0.16299.0"); vcxProj./**/ WriteElementString("WindowsTargetPlatformMinVersion", "10.0.10586.0"); vcxProj./**/ WriteElementString("ApplicationTypeRevision", "10"); } vcxProj.WriteEndElement(); } } // ------------------------------------------------------------------------------------------ // Overriding main configuration properties. // ------------------------------------------------------------------------------------------ foreach (var platform in TargetInfo.EnumerateAllPlatforms()) { var platformString = ConvertPlatformToMsBuildPlatform(platform); var platformToolset = "v141"; foreach (var configuration in TargetInfo.EnumerateAllConfigurations()) { var configurationName = string.Concat(configuration, platform); vcxProj.WriteStartElement("PropertyGroup"); vcxProj./**/ WriteAttributeStringFormat("Condition", "'$(Configuration)|$(Platform)'=='{0}|{1}'", configurationName, platformString); vcxProj./**/ WriteAttributeString("Label", "Configuration"); vcxProj./**/ WriteElementString("PlatformToolset", platformToolset); if (IsPlatformNativelySupported(platform)) { vcxProj./**/ WriteElementString("ConfigurationType", project.BuildType[platform, configuration].ToString()); vcxProj./**/ WriteElementString("CharacterSet", "Unicode"); } else { vcxProj./**/ WriteElementString("ConfigurationType", "Makefile"); } vcxProj.WriteEndElement(); } } vcxProj.WriteStartElement("Import"); vcxProj./**/ WriteAttributeString("Project", @"$(VCTargetsPath)\Microsoft.Cpp.Default.props"); vcxProj.WriteEndElement(); vcxProj.WriteStartElement("Import"); vcxProj./**/ WriteAttributeString("Project", @"$(VCTargetsPath)\Microsoft.Cpp.props"); vcxProj.WriteEndElement(); vcxProj.WriteStartElement("ImportGroup"); vcxProj./**/ WriteAttributeString("Label", "ExtensionSettings"); vcxProj./**/ WriteStartElement("Import"); vcxProj./**//**/ WriteAttributeString("Project", @"$(VCTargetsPath)\BuildCustomizations\masm.props"); vcxProj./**/ WriteEndElement(); vcxProj.WriteEndElement(); vcxProj.WriteStartElement("PropertyGroup"); vcxProj./**/ WriteAttributeString("Label", "UserMacros"); vcxProj.WriteEndElement(); vcxProj.WriteStartElement("PropertyGroup"); vcxProj./**/ WriteElementString("NMakeForcedIncludes", "$(NMakeForcedIncludes)"); vcxProj./**/ WriteElementString("NMakeAssemblySearchPath", "$(NMakeAssemblySearchPath)"); vcxProj./**/ WriteElementString("NMakeForcedUsingAssemblies", "$(NMakeForcedUsingAssemblies)"); vcxProj.WriteEndElement(); // ------------------------------------------------------------------------------------------ // Overriding VC++ compiler and linker properties OR NMake commands.. // ------------------------------------------------------------------------------------------ foreach (var platform in TargetInfo.EnumerateAllPlatforms()) { var platformInfo = TargetPlatformInfo.Get(platform); var platformString = ConvertPlatformToMsBuildPlatform(platform); // Building list of header directories. var includePathes = project.GenerateIncludePaths(platform, TargetConfiguration.Debug); var standardLibrariesPaths = platformInfo.GenerateStandardIncludePaths(); if (string.IsNullOrEmpty(standardLibrariesPaths)) { standardLibrariesPaths = "$(NMakeIncludeSearchPath)"; } foreach (var configuration in TargetInfo.EnumerateAllConfigurations()) { var configurationInfo = TargetConfigurationInfo.Get(configuration); // Defining macros.. //var macros = project.GenerateMacros(platform, configuration); //var standardMacros = platformInfo.GenerateStandardMacrosList(); //if (string.IsNullOrEmpty(standardMacros)) //{ // standardMacros = "$(NMakePreprocessorDefinitions)"; //} // Collecting linked libraries.. var linkedLibraries = project.GenerateLinkedLibrariesPaths(platform, configuration); var standardLinkedLibraries = platformInfo.GenerateStandardLinkedLibrariesPaths(); if (string.IsNullOrEmpty(standardLinkedLibraries)) { standardLinkedLibraries = ""; } standardLinkedLibraries += "%(AdditionalDependencies)"; var configurationName = string.Concat(configuration, platform); var outputPath = project.OutputPath[platform, configuration]; vcxProj.WriteStartElement("ImportGroup"); vcxProj./**/ WriteAttributeStringFormat("Condition", "'$(Configuration)|$(Platform)'=='{0}|{1}'", configurationName, platformString); vcxProj./**/ WriteAttributeString("Label", "PropertySheets"); vcxProj./**/ WriteStartElement("Import"); vcxProj./**//**/ WriteAttributeString("Project", @"$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"); vcxProj./**//**/ WriteAttributeString("Condition", @"exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"); vcxProj./**//**/ WriteAttributeString("Label", "LocalAppDataPlatform"); vcxProj./**/ WriteEndElement(); vcxProj.WriteEndElement(); if (IsPlatformNativelySupported(platform)) { // ------------------------------------------------------------------------------------------ // Defining VC++ compiler and linker properties. // ------------------------------------------------------------------------------------------ // Include paths.. vcxProj.WriteStartElement("PropertyGroup"); vcxProj./**/ WriteAttributeStringFormat("Condition", "'$(Configuration)|$(Platform)'=='{0}|{1}'", configurationName, platformString); vcxProj./**/ WriteElementString("IncludePath", includePathes + "$(IncludePath)"); vcxProj./**/ WriteElementString("OutDir", Path.GetDirectoryName(outputPath) + Path.DirectorySeparatorChar); vcxProj./**/ WriteElementString("TargetName", Path.GetFileNameWithoutExtension(outputPath)); vcxProj.WriteEndElement(); // Compiler properties.. vcxProj.WriteStartElement("ItemDefinitionGroup"); vcxProj./**/ WriteAttributeStringFormat("Condition", "'$(Configuration)|$(Platform)'=='{0}|{1}'", configurationName, platformString); vcxProj./**/ WriteStartElement("ClCompile"); vcxProj./**//**/ WriteElementString("FavorSizeOrSpeed", "Speed"); vcxProj./**//**/ WriteElementString("PrecompiledHeader", "NotUsing"); vcxProj./**//**/ WriteElementString("CallingConvention", "FastCall"); vcxProj./**//**/ WriteElementString("WarningLevel", "Level" + TargetCppCompilerInformation.WarningLevel); vcxProj./**//**/ WriteElementString("TreatWarningAsError", TargetCppCompilerInformation.WarningsAreErrors.ToString()); //vcxProj./**//**/WriteElementString("PreprocessorDefinitions", macros + "%(PreprocessorDefinition)"); vcxProj./**//**/ WriteElementString("Optimization", configurationInfo.Optimize ? "Full" : "Disabled"); if (configurationInfo.IsDebug) { vcxProj.WriteElementString("RuntimeLibrary", "MultiThreadedDebugDLL"); } if (!platformInfo.RequiresRtti) { vcxProj.WriteElementString("RuntimeTypeInfo", "false"); } if (!platformInfo.RequiresExceptions) { //vcxProj.WriteElementString("ExceptionHandling", "false"); } vcxProj./**/ WriteEndElement(); // </ VC++ COMPILER PROPERTIES // Linker properties. vcxProj./**/ WriteStartElement("Link"); vcxProj./**//**/ WriteElementString("AdditionalDependencies", linkedLibraries + standardLinkedLibraries); vcxProj./**//**/ WriteElementString("GenerateDebugInformation", configurationInfo.GenerateDebugInformation.ToString().ToLowerInvariant()); if (project.BuildType[platform, configuration] == ProjectBuildType.DynamicLibrary) { vcxProj./**//**/ WriteElementString("ImportLibrary", project.ImportLibraryOutputPath[platform, configuration]); } vcxProj./**/ WriteEndElement(); vcxProj.WriteEndElement(); } else { // ------------------------------------------------------------------------------------------ // Defining NMake properties.. // ------------------------------------------------------------------------------------------ var nmakeCommand = $"\"{System.Reflection.Assembly.GetExecutingAssembly().Location}\" --compile-project \"{project.Source}\" {platform} {configuration} "; vcxProj.WriteStartElement("PropertyGroup"); vcxProj./**/ WriteAttributeStringFormat("Condition", "'$(Configuration)|$(Platform)'=='{0}|{1}'", configurationName, platformString); vcxProj./**/ WriteElementString("OutDir", Path.GetDirectoryName(outputPath) + Path.DirectorySeparatorChar); vcxProj./**/ WriteElementString("NMakeOutput", outputPath); //vcxProj./**/WriteElementString("NMakePreprocessorDefinitions", macros + standardMacros + ";GD_PLATFORNOT_WINDOWS=1"); vcxProj./**/ WriteElementString("NMakeIncludeSearchPath", includePathes + standardLibrariesPaths); vcxProj./**/ WriteElementString("NMakeBuildCommandLine", nmakeCommand); vcxProj./**/ WriteElementString("NMakeReBuildCommandLine", nmakeCommand + "Rebuild"); vcxProj./**/ WriteElementString("NMakeCleanCommandLine", nmakeCommand + "Clean"); vcxProj.WriteEndElement(); } } } vcxProj.WriteStartElement("Import"); vcxProj./**/ WriteAttributeString("Project", @"$(VCTargetsPath)\Microsoft.Cpp.targets"); vcxProj.WriteEndElement(); vcxProj.WriteStartElement("ImportGroup"); vcxProj./**/ WriteAttributeString("Label", "ExtensionSettings"); vcxProj./**/ WriteStartElement("Import"); vcxProj./**//**/ WriteAttributeString("Project", @"$(VCTargetsPath)\BuildCustomizations\masm.targets"); vcxProj./**/ WriteEndElement(); vcxProj.WriteEndElement(); vcxProj./**/ WriteEndElement(); vcxProj.WriteEndDocument(); } // ========================================================================================== // Generating VCPROJ.FILTERS files. // ========================================================================================== var vcxProjFiltersPath = vcxProjPath + ".filters"; using (var vcxProjFilters = new XmlTextWriter(vcxProjFiltersPath, null) { Formatting = Formatting.Indented, Indentation = 1, IndentChar = '\t' }) { vcxProjFilters.WriteStartDocument(); vcxProjFilters./**/ WriteStartElement("Project", c_MsBuild2003Namespace); vcxProjFilters./**/ WriteAttributeString("ToolsVersion", c_ToolsFiltersVersion); var projectFoldersCache = new HashSet <string>(); foreach (var projectSourceFile in project.Files[TargetPlatform.Any, TargetConfiguration.Any]) { var projectSourceFileDirectory = Path.GetDirectoryName(projectSourceFile.FilePath); string projectSourceFilter; // Checking if this source can have a filter: it is located in project directory and it not in it's root. // We also add "Source" to each filter to separate configuration and source files. if (projectSourceFileDirectory != null && projectSourceFileDirectory.StartsWith(project.SourcesFilterOrigin, StringComparison.InvariantCultureIgnoreCase) && projectSourceFileDirectory.Length > project.SourcesFilterOrigin.Length + 1) { projectSourceFilter = "Source\\" + projectSourceFileDirectory.Substring(project.SourcesFilterOrigin.Length + 1); var projectSourceFilterIter = projectSourceFilter; while (!projectFoldersCache.Contains(projectSourceFilterIter) && !string.IsNullOrEmpty(projectSourceFilterIter)) { // ------------------------------------------------------------------------------------------ // Defining group of filters. // ------------------------------------------------------------------------------------------ vcxProjFilters.WriteStartElement("ItemGroup"); vcxProjFilters./**/ WriteStartElement("Filter"); vcxProjFilters./**//**/ WriteAttributeString("Include", projectSourceFilterIter); vcxProjFilters./**//**/ WriteElementString("UniqueIdentifier", CreateMsBuildGuid()); vcxProjFilters./**/ WriteEndElement(); vcxProjFilters.WriteEndElement(); projectFoldersCache.Add(projectSourceFilterIter); projectSourceFilterIter = Path.GetDirectoryName(projectSourceFilterIter); } } else { projectSourceFilter = "Source"; } // ------------------------------------------------------------------------------------------ // Defining source's filters. // ------------------------------------------------------------------------------------------ vcxProjFilters.WriteStartElement("ItemGroup"); vcxProjFilters./**/ WriteStartElement(ConvertFileTypeToVcxProjElement(projectSourceFile.FileType)); vcxProjFilters./**//**/ WriteAttributeString("Include", projectSourceFile.FilePath); vcxProjFilters./**//**/ WriteElementString("Filter", projectSourceFilter); vcxProjFilters./**/ WriteEndElement(); vcxProjFilters.WriteEndElement(); } vcxProjFilters./**/ WriteEndElement(); vcxProjFilters.WriteEndDocument(); } return(vcxProjPath); }
} // class PbxGroup /// <summary> /// Generates project files for XCode: '.xcodeproj'. /// </summary> /// <param name="project">Parsed project object.</param> /// <returns>Path to XCode '.xcodeproj' file.</returns> /// <inheritdoc /> public sealed override string GenerateProjectFiles(Project project) { var xcodeProjPath = Path.Combine(base.GenerateProjectFiles(project), project.Name) + ".xcodeproj"; var xcodePbxProjPath = Path.Combine(xcodeProjPath, "project.pbxproj"); Directory.CreateDirectory(xcodeProjPath); using (var xcodeProj = new StreamWriter(xcodePbxProjPath)) { // ========================================================================================== // Begin .XCODEPROJ.PBXPROJ // ========================================================================================== var platform = TargetPlatform.MacOS; //var configuration = TargetConfiguration.Debug; xcodeProj.WriteLine(@"// !$*UTF8*$! // Generated by GoddamnBuildSystem. Please, do not edit this file manually. { archiveVersion = 1; classes = { }; objectVersion = 48; objects = {"); // ------------------------------------------------------------------------------------------ // Referenced files section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin PBXFileReference section */"); // Writing down product reference.. string xcodeProductExplicitFileType; switch (project.BuildType[platform, TargetConfiguration.Debug]) { case ProjectBuildType.Application: xcodeProductExplicitFileType = "compiled.mach-o.executable"; break; case ProjectBuildType.StaticLibrary: xcodeProductExplicitFileType = "compiled.mach-o.a"; break; case ProjectBuildType.DynamicLibrary: xcodeProductExplicitFileType = "compiled.mach-o.dylib"; break; default: throw new NotSupportedException(); } var xcodeProductRefUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeProductRefUUID} /*{project.Name}*/ = {{ isa = PBXFileReference; explicitFileType = ""{xcodeProductExplicitFileType}""; includeInIndex = 0; path = {project.Name}; sourceTree = BUILT_PRODUCTS_DIR; }};"); // Writing down referenced dependencies.. foreach (var projectDependency in project.Dependencies[platform, TargetConfiguration.Any]) { foreach (var projectDependencyFramework in projectDependency.LinkedLibraries[platform, TargetConfiguration.Debug]) { string xcodeDependencyRefLastKnownType; switch (projectDependencyFramework.FileType) { case DependencyFileType.StaticLibrary: xcodeDependencyRefLastKnownType = "archive.ar"; break; case DependencyFileType.DynamicLibrary: xcodeDependencyRefLastKnownType = "compiled.mach-o.dylib"; break; default: throw new NotSupportedException(); } projectDependencyFramework.FileMisc.RefUUID = new PbxUUID(); xcodeProj.WriteLine($@" {projectDependencyFramework.FileMisc.RefUUID} /*{projectDependencyFramework.FilePath}*/ = {{ isa = PBXFileReference; lastKnownFileType = {xcodeDependencyRefLastKnownType}; path = {projectDependencyFramework.FilePath}; name = {Path.GetFileName(projectDependencyFramework.FilePath)}; sourceTree = ""<group>""; }};"); } } // Writing down referenced files.. foreach (var projectSource in project.Files[platform, TargetConfiguration.Any]) { string xcodeFileRefLastKnownType; switch (projectSource.FileType) { case ProjectSourceFileType.SourceCode: xcodeFileRefLastKnownType = "sourcecode.cpp.cpp"; break; case ProjectSourceFileType.SourceCodeObjective: xcodeFileRefLastKnownType = "sourcecode.cpp.objcpp"; break; case ProjectSourceFileType.SourceCodeAssembler: xcodeFileRefLastKnownType = "sourcecode.asm"; break; case ProjectSourceFileType.HeaderFile: case ProjectSourceFileType.InlineImplementation: xcodeFileRefLastKnownType = "sourcecode.cpp.h"; break; case ProjectSourceFileType.ResourceScript: xcodeFileRefLastKnownType = "file.txt"; break; default: throw new NotSupportedException(); } projectSource.FileMisc.RefUUID = new PbxUUID(); xcodeProj.WriteLine($@" {projectSource.FileMisc.RefUUID} /*{projectSource.FilePath}*/ = {{ isa = PBXFileReference; lastKnownFileType = {xcodeFileRefLastKnownType}; path = {projectSource.FilePath}; name = {Path.GetFileName(projectSource.FilePath)}; sourceTree = ""<group>""; }};"); } xcodeProj.WriteLine("/* End PBXFileReference section */\n"); // ------------------------------------------------------------------------------------------ // Build Files section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin PBXBuildFile section */"); // Product.. var xcodeProductBuildUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeProductBuildUUID} /*{project.Name}*/ = {{ isa = PBXBuildFile; fileRef = {xcodeProductRefUUID}; }};"); // Frameworks.. foreach (var projectDependency in project.Dependencies[platform, TargetConfiguration.Any]) { foreach (var projectDependencyFramework in projectDependency.LinkedLibraries[platform, TargetConfiguration.Debug]) { projectDependencyFramework.FileMisc.BuildUUID = new PbxUUID(); xcodeProj.WriteLine($@" {projectDependencyFramework.FileMisc.BuildUUID} /*{projectDependencyFramework.FilePath}*/ = {{ isa = PBXBuildFile; fileRef = {projectDependencyFramework.FileMisc.RefUUID}; }};"); } } // Source files.. foreach (var projectSource in project.Files[platform, TargetConfiguration.Any]) { // Writing down only files that need to be compiled.. switch (projectSource.FileType) { case ProjectSourceFileType.SourceCode: case ProjectSourceFileType.SourceCodeObjective: case ProjectSourceFileType.SourceCodeAssembler: break; default: continue; } projectSource.FileMisc.BuildUUID = new PbxUUID(); xcodeProj.WriteLine($@" {projectSource.FileMisc.BuildUUID} /*{projectSource.FilePath}*/ = {{ isa = PBXBuildFile; fileRef = {projectSource.FileMisc.RefUUID}; }};"); } xcodeProj.WriteLine("/* End PBXBuildFile section */\n"); // ------------------------------------------------------------------------------------------ // Group section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin PBXGroup section */"); // Generating groups for sources.. var xcodeGroupSource = new PbxGroup(); foreach (var projectSource in project.Files[platform, TargetConfiguration.Any]) { var projectSourceDirectory = Path.GetDirectoryName(projectSource.FilePath); if (projectSourceDirectory != null && projectSourceDirectory.StartsWith(project.SourcesFilterOrigin, StringComparison.Ordinal) && projectSourceDirectory.Length > project.SourcesFilterOrigin.Length + 1) { // Source would be added to some child group. var projectSourceGroupPath = projectSourceDirectory.Substring(project.SourcesFilterOrigin.Length + 1); var projectSourceGroupPathSplitted = projectSourceGroupPath.Trim().Split(Path.DirectorySeparatorChar); xcodeGroupSource.Add(projectSourceGroupPathSplitted, projectSource.FileMisc.RefUUID); } else { // Source would be added to the root group. xcodeGroupSource.Add(projectSource.FileMisc.RefUUID); } } // Writing child groups.. xcodeGroupSource.Traverse((xcodeProjGroupName, xcodeProjGroup) => { var xcodeGroupChildrenUUIDList = new StringBuilder(); foreach (var xcodeProjGroupChildPair in xcodeProjGroup.ChildGroups) { xcodeGroupChildrenUUIDList.Append($"{xcodeProjGroupChildPair.Value.UUID}, "); } foreach (var xcodeProjGroupChildPair in xcodeProjGroup.ChildFileRefs) { xcodeGroupChildrenUUIDList.Append($"{xcodeProjGroupChildPair}, "); } // ReSharper disable once AccessToDisposedClosure xcodeProj.WriteLine($@" {xcodeProjGroup.UUID} = {{ isa = PBXGroup; children = ( {xcodeGroupChildrenUUIDList} ); path = ""{xcodeProjGroupName ?? "Source"}""; sourceTree = ""<group>""; }};"); }); // Writing products group.. var xcodeGroupProductsUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeGroupProductsUUID} /*Products*/ = {{ isa = PBXGroup; children = ( {xcodeProductRefUUID} /*{project.Name}*/, ); name = Products; sourceTree = ""<group>""; }};"); // Writing frameworks group.. var xcodeGroupFrameworksChildrenUUIDList = new StringBuilder(); foreach (var projectDependency in project.Dependencies[platform, TargetConfiguration.Any]) { foreach (var projectDependencyFramework in projectDependency.LinkedLibraries[platform, TargetConfiguration.Any]) { xcodeGroupFrameworksChildrenUUIDList.Append($"{projectDependencyFramework.FileMisc.RefUUID}, "); } } var xcodeGroupFrameworksUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeGroupFrameworksUUID} /*Products*/ = {{ isa = PBXGroup; children = ( {xcodeGroupFrameworksChildrenUUIDList} ); name = Frameworks; sourceTree = ""<group>""; }};"); // Writing root group.. var xcodeGroupRootUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeGroupRootUUID} = {{ isa = PBXGroup; children = ( {xcodeGroupSource.UUID} /*Source*/, {xcodeGroupProductsUUID} /*Products*/, {xcodeGroupFrameworksUUID} /*Frameworks*/, ); sourceTree = ""<group>""; usesTabs = 0; }};"); xcodeProj.WriteLine("/* End PBXGroup section */\n"); // ------------------------------------------------------------------------------------------ // Source build phase section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin PBXSourcesBuildPhase section */"); var xcodeBuildPhaseSourcesUUIDList = new StringBuilder(); foreach (var projectSource in project.Files[platform, TargetConfiguration.Any]) { // Writing down only files that need to be compiled.. switch (projectSource.FileType) { case ProjectSourceFileType.SourceCode: case ProjectSourceFileType.SourceCodeObjective: case ProjectSourceFileType.SourceCodeAssembler: break; default: continue; } xcodeBuildPhaseSourcesUUIDList.Append($"{projectSource.FileMisc.BuildUUID},"); } var xcodeBuildPhaseSourcesUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeBuildPhaseSourcesUUID} /*Sources*/ = {{ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( {xcodeBuildPhaseSourcesUUIDList} ); runOnlyForDeploymentPostprocessing = 0; }};"); xcodeProj.WriteLine("/* End PBXSourcesBuildPhase section */\n"); // ------------------------------------------------------------------------------------------ // Framework build phase section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin PBXFrameworksBuildPhase section */"); var xcodeBuildPhaseFrameworksUUIDList = new StringBuilder(); foreach (var projectDependency in project.Dependencies[platform, TargetConfiguration.Any]) { foreach (var projectDependencyFramework in projectDependency.LinkedLibraries[platform, TargetConfiguration.Debug]) { xcodeBuildPhaseFrameworksUUIDList.Append($"{projectDependencyFramework.FileMisc.BuildUUID},"); } } var xcodeBuildPhaseFrameworksUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeBuildPhaseFrameworksUUID} /*Frameworks*/ = {{ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( {xcodeBuildPhaseFrameworksUUIDList} ); runOnlyForDeploymentPostprocessing = 0; }};"); xcodeProj.WriteLine("/* End PBXFrameworksBuildPhase section */\n"); // ------------------------------------------------------------------------------------------ // Copy files build phase section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin PBXCopyFilesBuildPhase section */"); // TODO: Copy files from dependencies. var xcodeBuildPhaseCopyFilesUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeBuildPhaseCopyFilesUUID} /*Copy Files*/ = {{ isa = PBXCopyFilesBuildPhase; buildActionMask = 12; dstPath = ""{Path.GetDirectoryName(project.OutputPath[platform, TargetConfiguration.Debug])}""; dstSubfolderSpec = 0; files = ( {xcodeProductBuildUUID}, /*Product*/ ); runOnlyForDeploymentPostprocessing = 0; }};"); xcodeProj.WriteLine("/* End PBXCopyFilesBuildPhase section */\n"); // ------------------------------------------------------------------------------------------ // Target configuration section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin XCBuildConfiguration section */"); var xcodeConfigurationNativeTargetUUIDList = new StringBuilder(); var xcodeConfigurationProjectUUIDList = new StringBuilder(); foreach (var configuration in TargetInfo.EnumerateAllConfigurations()) { var xcodeConfigurationNativeTargetUUID = new PbxUUID(); xcodeConfigurationNativeTargetUUIDList.Append($"{xcodeConfigurationNativeTargetUUID}, "); // TODO: Configure this shit correctly! xcodeProj.WriteLine($@" {xcodeConfigurationNativeTargetUUID} /*{configuration}*/ = {{ isa = XCBuildConfiguration; name = {configuration}; buildSettings = {{ CODE_SIGN_STYLE = Automatic; HEADER_SEARCH_PATHS = ( {project.GenerateIncludePaths(platform, configuration, ", ")} ); LIBRARY_SEARCH_PATHS = ( ""$(inherited)"", {project.GenerateLinkedLibrariesPaths(platform, configuration, ", ", Path.GetDirectoryName)} ); PRODUCT_NAME = ""$(TARGET_NAME)""; }}; }};"); var xcodeConfigurationProjectUUID = new PbxUUID(); xcodeConfigurationProjectUUIDList.Append($"{xcodeConfigurationProjectUUID}, "); // TODO: Configure this shit correctly! xcodeProj.WriteLine($@" {xcodeConfigurationProjectUUID} /*{configuration}*/ = {{ isa = XCBuildConfiguration; name = {configuration}; buildSettings = {{ ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = ""gnu++14""; CLANG_CXX_LIBRARY = ""libc++""; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""-""; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( ""DEBUG=1"", ""$(inherited)"", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.13; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }}; }};"); } xcodeProj.WriteLine("/* End XCBuildConfiguration section */\n"); // ------------------------------------------------------------------------------------------ // Configuration List section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin XCConfigurationList section */"); var xcodeConfigurationListNativeTarget = new PbxUUID(); var xcodeConfigurationListProjectUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeConfigurationListNativeTarget} /*PBXNativeTarget*/ = {{ isa = XCConfigurationList; buildConfigurations = ( {xcodeConfigurationNativeTargetUUIDList} ); defaultConfigurationIsVisible = 0; defaultConfigurationName = {TargetConfiguration.Release}; }};"); xcodeProj.WriteLine($@" {xcodeConfigurationListProjectUUID} /*PBXProject*/ = {{ isa = XCConfigurationList; buildConfigurations = ( {xcodeConfigurationProjectUUIDList} ); defaultConfigurationIsVisible = 0; defaultConfigurationName = {TargetConfiguration.Release}; }};"); xcodeProj.WriteLine("/* End XCConfigurationList section */\n"); // ------------------------------------------------------------------------------------------ // Native Target section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin PBXNativeTarget section */"); string xcodeNativeTargetProductType; switch (project.BuildType[TargetPlatform.MacOS, TargetConfiguration.Release]) { case ProjectBuildType.Application: // TODO: Actually, here should be .application. // But this requires to embed a plist. xcodeNativeTargetProductType = "com.apple.product-type.tool"; break; case ProjectBuildType.StaticLibrary: xcodeNativeTargetProductType = "com.apple.product-type.library.static"; break; case ProjectBuildType.DynamicLibrary: xcodeNativeTargetProductType = "com.apple.product-type.library.dynamic"; break; default: throw new NotSupportedException(); } var xcodeNativeTargetUUID = project.Misc.NativeTargetUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeNativeTargetUUID} /*{project.Name}*/ = {{ isa = PBXNativeTarget; buildConfigurationList = {xcodeConfigurationListNativeTarget}; buildPhases = ( {xcodeBuildPhaseSourcesUUID}, /*Sources*/ {xcodeBuildPhaseFrameworksUUID}, /* Frameworks */ {xcodeBuildPhaseCopyFilesUUID}, /* Copy Files */ ); buildRules = ( ); dependencies = ( ); name = {project.Name}; productName = ""{project.Name}""; productReference = {xcodeProductRefUUID}; productType = ""{xcodeNativeTargetProductType}""; }};"); xcodeProj.WriteLine("/* End PBXNativeTarget section */\n"); // ------------------------------------------------------------------------------------------ // Project section. // ------------------------------------------------------------------------------------------ xcodeProj.WriteLine("/* Begin PBXProject section */"); var xcodeProjectUUID = project.Misc.ProjectUUID = new PbxUUID(); xcodeProj.WriteLine($@" {xcodeProjectUUID} /*{project.Name}*/ = {{ isa = PBXProject; attributes = {{ LastUpgradeCheck = 0920; ORGANIZATIONNAME = {Environment.UserName}; TargetAttributes = {{ 7CC50132201C539100AA2808 = {{ CreatedOnToolsVersion = 9.2; ProvisioningStyle = Automatic; }}; }}; }}; buildConfigurationList = {xcodeConfigurationListProjectUUID}; compatibilityVersion = ""Xcode 8.0""; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = {xcodeGroupRootUUID}; productRefGroup = {xcodeGroupProductsUUID}; projectDirPath = """"; projectRoot = """"; targets = ( {xcodeNativeTargetUUID} /*PBXNativeTarget*/ ); }};"); xcodeProj.WriteLine("/* End PBXProject section */\n"); // ========================================================================================== // End .XCODEPROJ.PBXPROJ // ========================================================================================== xcodeProj.WriteLine($@" }}; rootObject = {xcodeProjectUUID}; }}"); } return(xcodeProjPath); }