in Sharpmake.Generators/Apple/XCodeProj.cs [488:926]
private void PrepareSections(XCodeGenerationContext context, List<Project.Configuration> configurations)
{
Project project = context.Project;
Project.Configuration defaultConfiguration = configurations.Where(conf => conf.UseAsDefaultForXCode == true).FirstOrDefault() ?? configurations[0];
//TODO: add support for multiple targets with the same outputtype. Would need a mechanism to define a default configuration per target and associate it with non-default conf with different optimization.
//At the moment it only supports target with different output type (e.g:lib, app, test bundle)
//Note that we also separate FastBuild configurations
Dictionary<string, List<Project.Configuration>> projectTargetsList = GetProjectConfigurationsPerTarget(configurations);
//Directory structure
SetRootGroup(project, configurations[0]);
ProjectVariantGroup variantGroup = new ProjectVariantGroup();
_projectItems.Add(variantGroup);
Strings resourceFiles = new Strings(project.ResourceFiles);
Strings sourceFiles = new Strings(project.GetSourceFilesForConfigurations(configurations).Except(resourceFiles));
string workspacePath = Directory.GetParent(configurations[0].ProjectFullFileNameWithExtension).FullName;
//Generate options for each configuration
_optionMapping = new Dictionary<Project.Configuration, Options.ExplicitOptions>();
foreach (Project.Configuration configuration in configurations)
{
context.Configuration = configuration;
_optionMapping[configuration] = GenerateOptions(context);
}
_projectReferencesGroups = new Dictionary<ProjectFolder, ProjectReference>();
_nativeOrLegacyTargets = new Dictionary<string, ProjectTarget>();
_targetDependencies = new Dictionary<string, List<ProjectTargetDependency>>();
_sourcesBuildPhases = new Dictionary<string, ProjectSourcesBuildPhase>();
_resourcesBuildPhases = new Dictionary<string, ProjectResourcesBuildPhase>();
_frameworksBuildPhases = new Dictionary<string, ProjectFrameworksBuildPhase>();
_headersBuildPhases = new Dictionary<string, UniqueList<ProjectHeadersBuildPhase>>();
_copyFilesPreBuildPhases = new Dictionary<string, UniqueList<ProjectCopyFilesBuildPhase>>();
_copyFilesBuildPhases = new Dictionary<string, UniqueList<ProjectCopyFilesBuildPhase>>();
_copyFilesPostBuildPhases = new Dictionary<string, UniqueList<ProjectCopyFilesBuildPhase>>();
_shellScriptPreBuildPhases = new Dictionary<string, UniqueList<ProjectShellScriptBuildPhase>>();
_shellScriptPostBuildPhases = new Dictionary<string, UniqueList<ProjectShellScriptBuildPhase>>();
bool createUnitTestTarget = project.XcodeUnitTestSourceFiles.Count > 0 && configurations.All(conf => conf.Output == Project.Configuration.OutputType.AppleApp);
//Loop on each targets
foreach (var projectTarget in projectTargetsList)
{
string xCodeTargetName = projectTarget.Key;
var targetConfigurations = projectTarget.Value;
string xCodeUnitTestTargetName = project.XcodeUnitTestTargetName;
var configurationsForTarget = new HashSet<ProjectBuildConfiguration>();
var configurationListForNativeTarget = new ProjectConfigurationList(configurationsForTarget, xCodeTargetName);
_projectItems.Add(configurationListForNativeTarget);
HashSet<ProjectBuildConfiguration> configurationsForUnitTestTarget = null;
ProjectConfigurationList configurationListForUnitTestNativeTarget = null;
if (createUnitTestTarget)
{
configurationsForUnitTestTarget = new HashSet<ProjectBuildConfiguration>();
configurationListForUnitTestNativeTarget = new ProjectConfigurationList(configurationsForUnitTestTarget, xCodeUnitTestTargetName);
_projectItems.Add(configurationListForUnitTestNativeTarget);
}
var firstConf = targetConfigurations.First();
// Adjust fastbuild target
if (firstConf.IsFastBuild)
{
// Handle cases where a configuration is using fastbuild but not all configurations are using it(and the first one is not using it)
firstConf = targetConfigurations.FirstOrDefault(conf => conf.FastBuildMasterBffList.Any(), targetConfigurations[0]);
}
bool canIncludeSourceFiles = !firstConf.IsFastBuild || firstConf.Output != Project.Configuration.OutputType.AppleApp || !firstConf.XcodeUseNativeProjectForFastBuildApp;
if (canIncludeSourceFiles)
{
var projectSourcesBuildPhase = new ProjectSourcesBuildPhase(xCodeTargetName, 2147483647);
_projectItems.Add(projectSourcesBuildPhase);
_sourcesBuildPhases.Add(xCodeTargetName, projectSourcesBuildPhase);
}
if (createUnitTestTarget)
{
// currently, unit Test target won't use fastbuild
var projectUnitTestSourcesBuildPhase = new ProjectSourcesBuildPhase(xCodeUnitTestTargetName, 2147483647);
_projectItems.Add(projectUnitTestSourcesBuildPhase);
_sourcesBuildPhases.Add(xCodeUnitTestTargetName, projectUnitTestSourcesBuildPhase);
}
var resourceBuildPhase = new ProjectResourcesBuildPhase(xCodeTargetName, 2147483647);
_projectItems.Add(resourceBuildPhase);
_resourcesBuildPhases.Add(xCodeTargetName, resourceBuildPhase);
if (createUnitTestTarget)
{
var unitTestResourceBuildPhase = new ProjectResourcesBuildPhase(xCodeUnitTestTargetName, 2147483647);
_projectItems.Add(unitTestResourceBuildPhase);
_resourcesBuildPhases.Add(xCodeUnitTestTargetName, unitTestResourceBuildPhase);
}
var frameworkBuildPhase = new ProjectFrameworksBuildPhase(xCodeTargetName, 2147483647);
_projectItems.Add(frameworkBuildPhase);
_frameworksBuildPhases.Add(xCodeTargetName, frameworkBuildPhase);
if (createUnitTestTarget)
{
var unitTestFrameworkBuildPhase = new ProjectFrameworksBuildPhase(xCodeUnitTestTargetName, 2147483647);
_projectItems.Add(unitTestFrameworkBuildPhase);
_frameworksBuildPhases.Add(xCodeUnitTestTargetName, unitTestFrameworkBuildPhase);
}
var headersBuildPhases = new UniqueList<ProjectHeadersBuildPhase>();
_headersBuildPhases.Add(xCodeTargetName, headersBuildPhases);
var copyFilesPreBuildPhases = new UniqueList<ProjectCopyFilesBuildPhase>();
_copyFilesPreBuildPhases.Add(xCodeTargetName, copyFilesPreBuildPhases);
var copyFilesBuildPhases = new UniqueList<ProjectCopyFilesBuildPhase>();
_copyFilesBuildPhases.Add(xCodeTargetName, copyFilesBuildPhases);
var copyFilesPostBuildPhases = new UniqueList<ProjectCopyFilesBuildPhase>();
_copyFilesPostBuildPhases.Add(xCodeTargetName, copyFilesPostBuildPhases);
var targetDependencies = new List<ProjectTargetDependency>();
_targetDependencies.Add(xCodeTargetName, targetDependencies);
if (createUnitTestTarget)
{
var unitTestTargetDependencies = new List<ProjectTargetDependency>();
_targetDependencies.Add(xCodeUnitTestTargetName, unitTestTargetDependencies);
}
string masterBffFilePath = null;
if (canIncludeSourceFiles)
PrepareSourceFiles(xCodeTargetName, sourceFiles.SortedValues, project, defaultConfiguration, forUnitTest: false, workspacePath);
if (createUnitTestTarget)
{
PrepareSourceFiles(xCodeUnitTestTargetName, project.XcodeUnitTestSourceFiles, project, defaultConfiguration, forUnitTest: true, workspacePath);
}
PrepareResourceFiles(xCodeTargetName, resourceFiles.SortedValues, project, defaultConfiguration);
foreach (var conf in targetConfigurations)
{
PrepareExternalResourceFiles(xCodeTargetName, project, conf);
// AppleApp project for fastbuild, append fastbuild script into conf.EventPreBuild, so xcode will start fastbuild after all PreBuild phase and before build phase(resources, plist build etc.).
if (conf.IsFastBuild && conf.Output == Project.Configuration.OutputType.AppleApp)
{
var masterBff = conf.FastBuildMasterBffList.FirstOrDefault();
if (masterBff != null)
{
string fastBuildCommandLine = ProjectLegacyTarget.BuildArgumentsStringByCommandLineOptions(conf, true);
var fastbuildMakeArguments = FastBuildSettings.MakeCommandGenerator.GetArguments(FastBuildMakeCommandGenerator.BuildType.Build, conf, fastBuildCommandLine);
var fastbuildMakeExe = FastBuildSettings.MakeCommandGenerator.GetExecutablePath(conf);
var buildWorkingDirectory = Path.GetDirectoryName(masterBff);
var fastbuildCmd = @$"pushd {buildWorkingDirectory}
{fastbuildMakeExe} {fastbuildMakeArguments}
exit_code=$?
if (( $exit_code != 0 )); then
exit $exit_code
fi
popd";
conf.EventPreBuild.Add(fastbuildCmd);
}
}
RegisterScriptBuildPhase(xCodeTargetName, _shellScriptPreBuildPhases, conf.EventPreBuild.GetEnumerator());
RegisterScriptBuildPhase(xCodeTargetName, _shellScriptPostBuildPhases, conf.EventPostBuild.GetEnumerator());
if (conf.Output == Project.Configuration.OutputType.AppleFramework)
RegisterHeadersBuildPhase(xCodeTargetName, _headersBuildPhases);
var folderSpec = conf.Output == Project.Configuration.OutputType.Exe ? FolderSpec.AbsolutePath : FolderSpec.Resources;
RegisterCopyFilesBuildPhases(xCodeTargetName, _copyFilesBuildPhases, conf.ResolvedTargetCopyFiles.GetEnumerator(), conf.TargetCopyFilesPath, folderSpec);
RegisterCopyFilesBuildPhases(xCodeTargetName, _copyFilesBuildPhases, conf.ResolvedTargetCopyFilesToSubDirectory.GetEnumerator());
RegisterCopyFilesBuildPhases(xCodeTargetName, _copyFilesPostBuildPhases, conf.EventPostBuildCopies.GetEnumerator());
RegisterCopyFilesBuildPhases(xCodeTargetName, _copyFilesPreBuildPhases, conf.ResolvedEventPreBuildExe.GetEnumerator());
RegisterCopyFilesBuildPhases(xCodeTargetName, _copyFilesPreBuildPhases, conf.ResolvedEventCustomPreBuildExe.GetEnumerator());
RegisterCopyFilesBuildPhases(xCodeTargetName, _copyFilesPostBuildPhases, conf.ResolvedEventPostBuildExe.GetEnumerator());
RegisterCopyFilesBuildPhases(xCodeTargetName, _copyFilesPostBuildPhases, conf.ResolvedEventCustomPostBuildExe.GetEnumerator());
switch (conf.Output)
{
case Project.Configuration.OutputType.AppleApp:
case Project.Configuration.OutputType.AppleFramework:
case Project.Configuration.OutputType.AppleBundle:
case Project.Configuration.OutputType.IosTestBundle:
case Project.Configuration.OutputType.Exe:
case Project.Configuration.OutputType.Dll:
OrderableStrings systemFrameworks = new OrderableStrings(conf.XcodeSystemFrameworks);
systemFrameworks.AddRange(conf.XcodeDependenciesSystemFrameworks);
RegisterFrameworkBuildPhases<ProjectSystemFrameworkFile>(xCodeTargetName, _frameworksBuildPhases,
_frameworksFolder,
systemFrameworks,
(string systemFramework) => new ProjectSystemFrameworkFile(systemFramework),
createUnitTestTarget,
xCodeUnitTestTargetName
);
OrderableStrings developerFrameworks = new OrderableStrings(conf.XcodeDeveloperFrameworks);
developerFrameworks.AddRange(conf.XcodeDependenciesDeveloperFrameworks);
RegisterFrameworkBuildPhases<ProjectDeveloperFrameworkFile>(xCodeTargetName, _frameworksBuildPhases,
_frameworksFolder,
developerFrameworks,
(string developerFramework) => new ProjectDeveloperFrameworkFile(developerFramework),
createUnitTestTarget,
xCodeUnitTestTargetName
);
OrderableStrings userFrameworks = new OrderableStrings(conf.XcodeUserFrameworks);
userFrameworks.AddRange(conf.XcodeDependenciesUserFrameworks);
RegisterFrameworkBuildPhases<ProjectUserFrameworkFile>(xCodeTargetName, _frameworksBuildPhases,
_frameworksFolder,
userFrameworks,
(string userFramework) => new ProjectUserFrameworkFile(XCodeUtil.ResolveProjectPaths(project, userFramework), workspacePath),
createUnitTestTarget,
xCodeUnitTestTargetName
);
OrderableStrings embeddedFrameworks = new OrderableStrings(conf.XcodeEmbeddedFrameworks);
embeddedFrameworks.AddRange(conf.XcodeDependenciesEmbeddedFrameworks);
var embeddedFrameworkItems = RegisterFrameworkBuildPhases<ProjectEmbeddedFrameworkFile>(xCodeTargetName, _frameworksBuildPhases,
_embedFrameworksFolder,
embeddedFrameworks,
(string embeddedFramework) => new ProjectEmbeddedFrameworkFile(XCodeUtil.ResolveProjectPaths(project, embeddedFramework), workspacePath),
createUnitTestTarget,
xCodeUnitTestTargetName
);
RegisterFrameworkCopyFilesPhases(xCodeTargetName, _copyFilesPostBuildPhases,
_embedFrameworksFolder,
embeddedFrameworkItems,
(string embeddedFramework) => new ProjectEmbeddedFrameworkFile(XCodeUtil.ResolveProjectPaths(project, embeddedFramework), workspacePath)
);
break;
}
if (conf.IsFastBuild)
{
// master bff path
// we only support projects in one or no master bff, but in that last case just output a warning
foreach (string confMasterBff in conf.FastBuildMasterBffList)
{
if (masterBffFilePath == null)
{
masterBffFilePath = confMasterBff;
}
else if (masterBffFilePath != confMasterBff)
{
throw new Error("Project {0} has a fastbuild target that has distinct master bff, sharpmake only supports 1.", conf);
}
}
// Make the commandline written in the bff available, except the master bff -config
string commandLine = conf.GetFastBuildCommandLineArguments();
Bff.SetCommandLineArguments(conf, commandLine);
}
if (conf.Output == Project.Configuration.OutputType.IosTestBundle)
{
var testFrameworkItem = new ProjectDeveloperFrameworkFile(_unitTestFramework);
var buildFileItem = new ProjectBuildFile(testFrameworkItem);
if (_frameworksFolder != null)
_frameworksFolder.AddChildren(testFrameworkItem);
_projectItems.Add(testFrameworkItem);
_projectItems.Add(buildFileItem);
_frameworksBuildPhases[xCodeTargetName].Files.Add(buildFileItem);
}
}
if (createUnitTestTarget)
{
var testFrameworkItem = new ProjectDeveloperFrameworkFile(_unitTestFramework);
var buildFileItem = new ProjectBuildFile(testFrameworkItem);
if (_frameworksFolder != null)
_frameworksFolder.AddChildren(testFrameworkItem);
_projectItems.Add(testFrameworkItem);
_projectItems.Add(buildFileItem);
_frameworksBuildPhases[xCodeUnitTestTargetName].Files.Add(buildFileItem);
}
// use the first conf as file, but the target name
var targetOutputFile = new ProjectOutputFile(firstConf, xCodeTargetName);
_productsGroup.AddChildren(targetOutputFile);
_projectItems.Add(targetOutputFile);
ProjectOutputFile targetUnitTestOutputFile = null;
if (createUnitTestTarget)
{
targetUnitTestOutputFile = new ProjectOutputFile(firstConf, xCodeUnitTestTargetName, forTestBundle : true);
_productsGroup.AddChildren(targetUnitTestOutputFile);
_projectItems.Add(targetUnitTestOutputFile);
}
ProjectTarget target;
ProjectTarget targetUnitTest = null;
if (!firstConf.IsFastBuild || (firstConf.Output == Project.Configuration.OutputType.AppleApp && firstConf.XcodeUseNativeProjectForFastBuildApp))
{
target = new ProjectNativeTarget(xCodeTargetName, targetOutputFile, configurationListForNativeTarget, _targetDependencies[xCodeTargetName]);
if (createUnitTestTarget)
{
targetUnitTest = new ProjectNativeTarget(xCodeUnitTestTargetName, targetUnitTestOutputFile, configurationListForUnitTestNativeTarget, _targetDependencies[xCodeUnitTestTargetName]);
}
}
else
{
target = new ProjectLegacyTarget(xCodeTargetName, targetOutputFile, configurationListForNativeTarget, firstConf);
}
target.ResourcesBuildPhase = _resourcesBuildPhases[xCodeTargetName];
if (targetUnitTest != null)
{
targetUnitTest.ResourcesBuildPhase = _resourcesBuildPhases[xCodeUnitTestTargetName];
}
if (_sourcesBuildPhases.ContainsKey(xCodeTargetName))
target.SourcesBuildPhase = _sourcesBuildPhases[xCodeTargetName];
if (targetUnitTest != null && _sourcesBuildPhases.ContainsKey(xCodeUnitTestTargetName))
targetUnitTest.SourcesBuildPhase = _sourcesBuildPhases[xCodeUnitTestTargetName];
if (_frameworksBuildPhases.ContainsKey(xCodeTargetName))
target.FrameworksBuildPhase = _frameworksBuildPhases[xCodeTargetName];
if (targetUnitTest != null)
{
targetUnitTest.FrameworksBuildPhase = _frameworksBuildPhases[xCodeUnitTestTargetName];
}
if (_shellScriptPreBuildPhases.ContainsKey(xCodeTargetName))
target.ShellScriptPreBuildPhases = _shellScriptPreBuildPhases[xCodeTargetName];
if (_headersBuildPhases.ContainsKey(xCodeTargetName))
target.HeadersBuildPhases = _headersBuildPhases[xCodeTargetName];
if (_copyFilesPreBuildPhases.ContainsKey(xCodeTargetName))
target.CopyFilesPreBuildPhases = _copyFilesPreBuildPhases[xCodeTargetName];
if (_copyFilesBuildPhases.ContainsKey(xCodeTargetName))
target.CopyFilesBuildPhases = _copyFilesBuildPhases[xCodeTargetName];
if (_copyFilesPostBuildPhases.ContainsKey(xCodeTargetName))
target.CopyFilesPostBuildPhases = _copyFilesPostBuildPhases[xCodeTargetName];
if (_shellScriptPostBuildPhases.ContainsKey(xCodeTargetName))
target.ShellScriptPostBuildPhases = _shellScriptPostBuildPhases[xCodeTargetName];
configurationListForNativeTarget.RelatedItem = target;
_projectItems.Add(target);
_nativeOrLegacyTargets.Add(xCodeTargetName, target);
if (targetUnitTest != null)
{
configurationListForUnitTestNativeTarget.RelatedItem = targetUnitTest;
_projectItems.Add(targetUnitTest);
_nativeOrLegacyTargets.Add(xCodeUnitTestTargetName, targetUnitTest);
}
//Generate BuildConfigurations
foreach (Project.Configuration targetConf in targetConfigurations)
{
var options = _optionMapping[targetConf];
ProjectBuildConfigurationForTarget configurationForTarget = null;
if (targetConf.Output == Project.Configuration.OutputType.IosTestBundle)
configurationForTarget = new ProjectBuildConfigurationForUnitTestTarget(targetConf, target, options);
else if (!targetConf.IsFastBuild || (targetConf.Output == Project.Configuration.OutputType.AppleApp && targetConf.XcodeUseNativeProjectForFastBuildApp))
configurationForTarget = new ProjectBuildConfigurationForNativeTarget(targetConf, (ProjectNativeTarget)target, options);
else
configurationForTarget = new ProjectBuildConfigurationForLegacyTarget(targetConf, (ProjectLegacyTarget)target, options);
configurationsForTarget.Add(configurationForTarget);
_projectItems.Add(configurationForTarget);
if (targetUnitTest != null)
{
configurationForTarget = new ProjectBuildConfigurationForUnitTestTarget(targetConf, targetUnitTest, options);
configurationsForUnitTestTarget.Add(configurationForTarget);
_projectItems.Add(configurationForTarget);
}
}
}
// Generate dependencies for unit test targets.
var unitTestConfigs = new List<Project.Configuration>(configurations).FindAll(element => element.Output == Project.Configuration.OutputType.IosTestBundle);
if (unitTestConfigs != null && unitTestConfigs.Count != 0)
{
foreach (Project.Configuration unitTestConfig in unitTestConfigs)
{
Project.Configuration bundleLoadingAppConfiguration = FindBundleLoadingApp(configurations);
if (bundleLoadingAppConfiguration == null)
continue;
string key = GetTargetKey(bundleLoadingAppConfiguration);
if (!_nativeOrLegacyTargets.ContainsKey(key))
continue;
ProjectTarget target = _nativeOrLegacyTargets[key];
if (!(target is ProjectNativeTarget))
continue;
ProjectNativeTarget bundleLoadingAppTarget = (ProjectNativeTarget)_nativeOrLegacyTargets[key];
ProjectReference projectReference = new ProjectReference(ItemSection.PBXProject, bundleLoadingAppTarget.Identifier);
ProjectContainerProxy projectProxy = new ProjectContainerProxy(projectReference, bundleLoadingAppTarget, ProjectContainerProxy.Type.Target);
ProjectTargetDependency targetDependency = new ProjectTargetDependency(projectReference, projectProxy, bundleLoadingAppTarget);
_projectItems.Add(targetDependency);
((ProjectNativeTarget)_nativeOrLegacyTargets[GetTargetKey(unitTestConfig)]).Dependencies.Add(targetDependency);
}
}
if (createUnitTestTarget)
{
GenerateDependenciesForUnitTestTargets(project, configurations);
}
HashSet<ProjectBuildConfiguration> configurationsForProject = new HashSet<ProjectBuildConfiguration>();
ProjectConfigurationList configurationListForProject = new ProjectConfigurationList(configurationsForProject, "configurationListForProject");
_projectItems.Add(configurationListForProject);
//This loop will find the register to the sets _projectItems and configurationsForProject the first configuration for each optimization type that is contained in the configurations.
//Project options can only be set according to optimization types e.g: Debug, Release, Retail.
foreach (Project.Configuration configuration in configurations)
{
var options = _optionMapping[configuration];
ProjectBuildConfigurationForProject configurationForProject = new ProjectBuildConfigurationForProject(configuration, options);
configurationsForProject.Add(configurationForProject);
_projectItems.Add(configurationForProject);
}
bool iCloudSupport = (_optionMapping[configurations[0]]["iCloud"] == "1");
string developmentTeam = _optionMapping[configurations[0]]["DevelopmentTeam"];
string provisioningStyle = _optionMapping[configurations[0]]["ProvisioningStyle"];
var nativeOrLegacyTargets = new List<ProjectTarget>(_nativeOrLegacyTargets.Values);
_projectMain = new ProjectMain(project.Name, _mainGroup, configurationListForProject, nativeOrLegacyTargets, iCloudSupport, developmentTeam, provisioningStyle);
configurationListForProject.RelatedItem = _projectMain;
foreach (KeyValuePair<ProjectFolder, ProjectReference> referenceGroup in _projectReferencesGroups)
{
_projectMain.AddProjectDependency(referenceGroup.Key, referenceGroup.Value);
}
_projectItems.Add(_projectMain);
}