Preload external build configuration files at project load time.

This avoids requiring filesystem access to compute the target environment,
and fixes the layering of build configurations when determining the target
SDK for the build.
This commit is contained in:
Grant Paul 2016-09-03 01:27:12 -07:00
parent 08ffd17ecb
commit de2c950210
8 changed files with 131 additions and 108 deletions

View File

@ -49,10 +49,6 @@ private:
std::vector<std::string> _variants;
std::vector<std::string> _architectures;
private:
ext::optional<pbxsetting::XC::Config> _projectConfigurationFile;
ext::optional<pbxsetting::XC::Config> _targetConfigurationFile;
private:
std::string _workingDirectory;
std::unordered_map<pbxproj::PBX::BuildFile::shared_ptr, std::string> _buildFileDisambiguation;
@ -70,8 +66,6 @@ public:
pbxsetting::Environment const &environment,
std::vector<std::string> const &variants,
std::vector<std::string> const &architectures,
ext::optional<pbxsetting::XC::Config> const &projectConfigurationFile,
ext::optional<pbxsetting::XC::Config> const &targetConfigurationFile,
std::string const &workingDirectory,
std::unordered_map<pbxproj::PBX::BuildFile::shared_ptr, std::string> const &buildFileDisambiguation);
@ -148,19 +142,6 @@ public:
std::vector<std::string> const &architectures() const
{ return _architectures; }
public:
/*
* The configuration files loaded for the project in this configuration.
*/
ext::optional<pbxsetting::XC::Config> const &projectConfigurationFile() const
{ return _projectConfigurationFile; }
/*
* The configuration files loaded for this target and configuration.
*/
ext::optional<pbxsetting::XC::Config> const &targetConfigurationFile() const
{ return _targetConfigurationFile; }
public:
/*
* The working directory the target should be built in.

View File

@ -12,6 +12,7 @@
#include <pbxbuild/Base.h>
#include <pbxbuild/DerivedDataHash.h>
#include <pbxsetting/XC/Config.h>
namespace pbxsetting { class Environment; }
namespace libutil { class Filesystem; }
@ -35,6 +36,7 @@ private:
pbxproj::PBX::Project::shared_ptr _project;
std::vector<xcscheme::SchemeGroup::shared_ptr> _schemeGroups;
std::unordered_map<std::string, pbxproj::PBX::Project::shared_ptr> _projects;
std::unordered_map<pbxproj::XC::BuildConfiguration::shared_ptr, pbxsetting::XC::Config> _configs;
public:
WorkspaceContext(
@ -43,7 +45,8 @@ public:
xcworkspace::XC::Workspace::shared_ptr const &workspace,
pbxproj::PBX::Project::shared_ptr const &project,
std::vector<xcscheme::SchemeGroup::shared_ptr> const &schemeGroups,
std::unordered_map<std::string, pbxproj::PBX::Project::shared_ptr> const &projects);
std::unordered_map<std::string, pbxproj::PBX::Project::shared_ptr> const &projects,
std::unordered_map<pbxproj::XC::BuildConfiguration::shared_ptr, pbxsetting::XC::Config> const &configs);
~WorkspaceContext();
public:
@ -85,6 +88,12 @@ public:
std::unordered_map<std::string, pbxproj::PBX::Project::shared_ptr> const &projects() const
{ return _projects; }
/*
* All configuration files, including for all loaded projects and targets.
*/
std::unordered_map<pbxproj::XC::BuildConfiguration::shared_ptr, pbxsetting::XC::Config> const &configs() const
{ return _configs; }
public:
/*
* Find a project in the workspace. Could be the root project (for a legacy build), a project

View File

@ -22,6 +22,7 @@
namespace Build = pbxbuild::Build;
namespace Target = pbxbuild::Target;
using pbxbuild::WorkspaceContext;
using libutil::Filesystem;
using libutil::FSUtil;
@ -38,8 +39,6 @@ Environment(
pbxsetting::Environment const &environment,
std::vector<std::string> const &variants,
std::vector<std::string> const &architectures,
ext::optional<pbxsetting::XC::Config> const &projectConfigurationFile,
ext::optional<pbxsetting::XC::Config> const &targetConfigurationFile,
std::string const &workingDirectory,
std::unordered_map<pbxproj::PBX::BuildFile::shared_ptr, std::string> const &buildFileDisambiguation) :
_sdk (sdk),
@ -53,8 +52,6 @@ Environment(
_environment (environment),
_variants (variants),
_architectures (architectures),
_projectConfigurationFile(projectConfigurationFile),
_targetConfigurationFile (targetConfigurationFile),
_workingDirectory (workingDirectory),
_buildFileDisambiguation (buildFileDisambiguation)
{
@ -108,27 +105,24 @@ ConfigurationNamed(pbxproj::XC::ConfigurationList::shared_ptr const &configurati
return nullptr;
}
auto configurationIterator = std::find_if(configurationList->begin(), configurationList->end(), [&](pbxproj::XC::BuildConfiguration::shared_ptr buildConfiguration) -> bool {
return buildConfiguration->name() == configuration;
});
if (configurationIterator == configurationList->end()) {
return nullptr;
for (pbxproj::XC::BuildConfiguration::shared_ptr const &buildConfiguration : configurationList->buildConfigurations()) {
if (buildConfiguration->name() == configuration) {
return buildConfiguration;
}
}
return *configurationIterator;
return nullptr;
}
static ext::optional<pbxsetting::XC::Config>
LoadConfigurationFile(Filesystem const *filesystem, pbxsetting::Environment const &environment, std::string const &workingDirectory, pbxproj::XC::BuildConfiguration::shared_ptr const &buildConfiguration)
ConfigurationFile(WorkspaceContext const &workspaceContext, pbxproj::XC::BuildConfiguration::shared_ptr const &buildConfiguration)
{
if (buildConfiguration->baseConfigurationReference() == nullptr) {
return ext::nullopt;
auto it = workspaceContext.configs().find(buildConfiguration);
if (it != workspaceContext.configs().end()) {
return it->second;
}
pbxsetting::Value configurationValue = buildConfiguration->baseConfigurationReference()->resolve();
std::string configurationPath = FSUtil::ResolveRelativePath(environment.expand(configurationValue), workingDirectory);
return pbxsetting::XC::Config::Load(filesystem, environment, configurationPath);
return ext::nullopt;
}
static std::vector<std::string>
@ -282,59 +276,67 @@ Create(Build::Environment const &buildEnvironment, Build::Context const &buildCo
pbxproj::XC::BuildConfiguration::shared_ptr targetConfiguration;
ext::optional<pbxsetting::XC::Config> projectConfigurationFile;
ext::optional<pbxsetting::XC::Config> targetConfigurationFile;
{
// FIXME(grp): $(SRCROOT) must be set in order to find the xcconfig, but we need the xcconfig to know $(SDKROOT). So this can't
// use the default level order, because $(SRCROOT) comes below $(SDKROOT). Hack around this for now with a synthetic environment.
// It's also in the wrong order because project settings should be below the SDK, but are needed to *load* the xcconfig.
/*
* Create a synthetic build setting environment to determine the SDK to use. The real build
* setting environment will interleave in SDK build settings, but those aren't available until
* the target SDK is itself determined.
*/
pbxsetting::Environment determinationEnvironment = buildEnvironment.baseEnvironment();
/*
* Add build base settings.
*/
determinationEnvironment.insertFront(buildContext.baseSettings(), false);
/*
* Add project build settings.
*/
determinationEnvironment.insertFront(target->project()->settings(), false);
projectConfiguration = ConfigurationNamed(target->project()->buildConfigurationList(), buildContext.configuration());
if (projectConfiguration == nullptr) {
fprintf(stderr, "error: unable to find project configuration %s\n", buildContext.configuration().c_str());
return ext::nullopt;
}
determinationEnvironment.insertFront(target->project()->settings(), false);
determinationEnvironment.insertFront(projectConfiguration->buildSettings(), false);
pbxsetting::Environment projectActionEnvironment = determinationEnvironment;
projectActionEnvironment.insertFront(buildContext.actionSettings(), false);
for (pbxsetting::Level const &level : buildContext.overrideLevels()) {
projectActionEnvironment.insertFront(level, false);
}
projectConfigurationFile = LoadConfigurationFile(Filesystem::GetDefaultUNSAFE(), projectActionEnvironment, workingDirectory, projectConfiguration);
projectConfigurationFile = ConfigurationFile(buildContext.workspaceContext(), projectConfiguration);
if (projectConfigurationFile) {
determinationEnvironment.insertFront(projectConfigurationFile->level(), false);
}
determinationEnvironment.insertFront(projectConfiguration->buildSettings(), false);
/*
* Add target build settings.
*/
determinationEnvironment.insertFront(target->settings(), false);
targetConfiguration = ConfigurationNamed(target->buildConfigurationList(), buildContext.configuration());
if (targetConfiguration == nullptr) {
fprintf(stderr, "error: unable to find target configuration %s\n", buildContext.configuration().c_str());
return ext::nullopt;
}
determinationEnvironment.insertFront(target->settings(), false);
determinationEnvironment.insertFront(targetConfiguration->buildSettings(), false);
// FIXME(grp): Similar issue for the target xcconfig. These levels aren't complete (no platform) but are needed to *get* which SDK to use.
pbxsetting::Environment targetActionEnvironment = determinationEnvironment;
targetActionEnvironment.insertFront(buildContext.actionSettings(), false);
for (pbxsetting::Level const &level : buildContext.overrideLevels()) {
targetActionEnvironment.insertFront(level, false);
}
targetConfigurationFile = LoadConfigurationFile(Filesystem::GetDefaultUNSAFE(), targetActionEnvironment, workingDirectory, targetConfiguration);
targetConfigurationFile = ConfigurationFile(buildContext.workspaceContext(), targetConfiguration);
if (targetConfigurationFile) {
determinationEnvironment.insertFront(targetConfigurationFile->level(), false);
}
determinationEnvironment.insertFront(targetConfiguration->buildSettings(), false);
/*
* Add build override settings.
*/
determinationEnvironment.insertFront(buildContext.actionSettings(), false);
for (pbxsetting::Level const &level : buildContext.overrideLevels()) {
determinationEnvironment.insertFront(level, false);
}
/*
* All settings added; determine target SDK.
*/
std::string sdkroot = determinationEnvironment.resolve("SDKROOT");
sdk = buildEnvironment.sdkManager()->findTarget(sdkroot);
if (sdk == nullptr) {
@ -372,7 +374,9 @@ Create(Build::Environment const &buildEnvironment, Build::Context const &buildCo
}
}
// Now we have $(SDKROOT), and can make the real levels.
/*
* Now we have $(SDKROOT), and can make the real levels.
*/
pbxsetting::Environment environment = buildEnvironment.baseEnvironment();
environment.insertFront(buildSystem->defaultSettings(), true);
environment.insertFront(buildContext.baseSettings(), false);
@ -395,12 +399,18 @@ Create(Build::Environment const &buildEnvironment, Build::Context const &buildCo
environment.insertFront(ProductTypeLevel(productType), false);
}
/*
* Add project build settings.
*/
environment.insertFront(target->project()->settings(), false);
if (projectConfigurationFile) {
environment.insertFront(projectConfigurationFile->level(), false);
}
environment.insertFront(projectConfiguration->buildSettings(), false);
/*
* Add target build settings.
*/
environment.insertFront(target->settings(), false);
if (targetConfigurationFile) {
environment.insertFront(targetConfigurationFile->level(), false);
@ -447,8 +457,6 @@ Create(Build::Environment const &buildEnvironment, Build::Context const &buildCo
environment,
variants,
architectures,
projectConfigurationFile,
targetConfigurationFile,
workingDirectory,
buildFileDisambiguation);
}

View File

@ -24,13 +24,15 @@ WorkspaceContext(
xcworkspace::XC::Workspace::shared_ptr const &workspace,
pbxproj::PBX::Project::shared_ptr const &project,
std::vector<xcscheme::SchemeGroup::shared_ptr> const &schemeGroups,
std::unordered_map<std::string, pbxproj::PBX::Project::shared_ptr> const &projects) :
std::unordered_map<std::string, pbxproj::PBX::Project::shared_ptr> const &projects,
std::unordered_map<pbxproj::XC::BuildConfiguration::shared_ptr, pbxsetting::XC::Config> const &configs) :
_basePath (basePath),
_derivedDataHash(derivedDataHash),
_workspace (workspace),
_project (project),
_schemeGroups (schemeGroups),
_projects (projects)
_projects (projects),
_configs (configs)
{
}
@ -96,6 +98,13 @@ loadedFilePaths() const
}
}
/*
* Add all config file paths.
*/
for (auto const &entry : _configs) {
loadedFilePaths.push_back(entry.second.path());
}
return loadedFilePaths;
}
@ -143,7 +152,38 @@ LoadWorkspaceProjects(Filesystem const *filesystem, std::vector<pbxproj::PBX::Pr
}
static void
LoadNestedProjects(Filesystem const *filesystem, std::vector<pbxproj::PBX::Project::shared_ptr> *projects, pbxsetting::Environment const &baseEnvironment, std::vector<pbxproj::PBX::Project::shared_ptr> const &rootProjects)
LoadConfigurationFiles(
Filesystem const *filesystem,
std::unordered_map<pbxproj::XC::BuildConfiguration::shared_ptr, pbxsetting::XC::Config> *configs,
pbxsetting::Environment const &environment,
pbxproj::XC::ConfigurationList::shared_ptr const &configurationList)
{
if (configurationList == nullptr) {
return;
}
/*
* Load all configuration files in the list.
*/
for (pbxproj::XC::BuildConfiguration::shared_ptr const &buildConfiguration : configurationList->buildConfigurations()) {
if (pbxproj::PBX::FileReference::shared_ptr const &configurationReference = buildConfiguration->baseConfigurationReference()) {
std::string configurationPath = environment.expand(configurationReference->resolve());
/* Load the configuration file. */
if (ext::optional<pbxsetting::XC::Config> configuration = pbxsetting::XC::Config::Load(filesystem, environment, configurationPath)) {
configs->insert({ buildConfiguration, *configuration });
}
}
}
}
static void
LoadNestedProjects(
Filesystem const *filesystem,
std::vector<pbxproj::PBX::Project::shared_ptr> *projects,
std::unordered_map<pbxproj::XC::BuildConfiguration::shared_ptr, pbxsetting::XC::Config> *configs,
pbxsetting::Environment const &baseEnvironment,
std::vector<pbxproj::PBX::Project::shared_ptr> const &rootProjects)
{
std::vector<pbxproj::PBX::Project::shared_ptr> nestedProjects;
@ -159,6 +199,14 @@ LoadNestedProjects(Filesystem const *filesystem, std::vector<pbxproj::PBX::Proje
pbxsetting::Environment environment = baseEnvironment;
environment.insertFront(project->settings(), false);
/*
* Load project and target configurations.
*/
LoadConfigurationFiles(filesystem, configs, environment, project->buildConfigurationList());
for (pbxproj::PBX::Target::shared_ptr const &target : project->targets()) {
LoadConfigurationFiles(filesystem, configs, environment, target->buildConfigurationList());
}
/*
* Find the nested projects.
*/
@ -185,7 +233,7 @@ LoadNestedProjects(Filesystem const *filesystem, std::vector<pbxproj::PBX::Proje
/*
* Load nested projects of the nested projects.
*/
LoadNestedProjects(filesystem, projects, baseEnvironment, nestedProjects);
LoadNestedProjects(filesystem, projects, configs, baseEnvironment, nestedProjects);
}
}
@ -222,6 +270,7 @@ Workspace(Filesystem const *filesystem, pbxsetting::Environment const &baseEnvir
{
std::vector<pbxproj::PBX::Project::shared_ptr> projects;
std::vector<xcscheme::SchemeGroup::shared_ptr> schemeGroups;
std::unordered_map<pbxproj::XC::BuildConfiguration::shared_ptr, pbxsetting::XC::Config> configs;
/*
* Add the schemes from the workspace itself.
@ -239,7 +288,7 @@ Workspace(Filesystem const *filesystem, pbxsetting::Environment const &baseEnvir
/*
* Recursively load nested projects within those projects.
*/
LoadNestedProjects(filesystem, &projects, baseEnvironment, projects);
LoadNestedProjects(filesystem, &projects, &configs, baseEnvironment, projects);
/*
* Load schemes for all projects, including nested projects.
@ -251,7 +300,7 @@ Workspace(Filesystem const *filesystem, pbxsetting::Environment const &baseEnvir
*/
DerivedDataHash derivedDataHash = DerivedDataHash::Create(workspace->projectFile());
return WorkspaceContext(workspace->basePath(), derivedDataHash, workspace, nullptr, schemeGroups, CreateProjectMap(projects));
return WorkspaceContext(workspace->basePath(), derivedDataHash, workspace, nullptr, schemeGroups, CreateProjectMap(projects), configs);
}
WorkspaceContext WorkspaceContext::
@ -259,6 +308,7 @@ Project(Filesystem const *filesystem, pbxsetting::Environment const &baseEnviron
{
std::vector<pbxproj::PBX::Project::shared_ptr> projects;
std::vector<xcscheme::SchemeGroup::shared_ptr> schemeGroups;
std::unordered_map<pbxproj::XC::BuildConfiguration::shared_ptr, pbxsetting::XC::Config> configs;
/*
* The root is a project, so it should be in the projects list.
@ -268,7 +318,7 @@ Project(Filesystem const *filesystem, pbxsetting::Environment const &baseEnviron
/*
* Recursively load nested projects within the project.
*/
LoadNestedProjects(filesystem, &projects, baseEnvironment, projects);
LoadNestedProjects(filesystem, &projects, &configs, baseEnvironment, projects);
/*
* Load schemes for all projects, including the root and nested projects.
@ -280,5 +330,5 @@ Project(Filesystem const *filesystem, pbxsetting::Environment const &baseEnviron
*/
DerivedDataHash derivedDataHash = DerivedDataHash::Create(project->projectFile());
return WorkspaceContext(project->basePath(), derivedDataHash, nullptr, project, schemeGroups, CreateProjectMap(projects));
return WorkspaceContext(project->basePath(), derivedDataHash, nullptr, project, schemeGroups, CreateProjectMap(projects), configs);
}

View File

@ -27,28 +27,13 @@ public:
ConfigurationList();
public:
inline void setDefaultConfigurationName(std::string const &name)
{ _defaultConfigurationName = name; }
inline BuildConfiguration::vector const &buildConfigurations() const
{ return _buildConfigurations; }
inline std::string const &defaultConfigurationName() const
{ return _defaultConfigurationName; }
public:
inline void setDefaultConfigurationIsVisible(bool visible)
{ _defaultConfigurationIsVisible = visible; }
inline bool defaultConfigurationIsVisible() const
{ return _defaultConfigurationIsVisible; }
public:
inline BuildConfiguration::vector::const_iterator begin() const
{ return _buildConfigurations.begin(); }
inline BuildConfiguration::vector::const_iterator end() const
{ return _buildConfigurations.end(); }
inline BuildConfiguration::vector::iterator begin()
{ return _buildConfigurations.begin(); }
inline BuildConfiguration::vector::iterator end()
{ return _buildConfigurations.end(); }
protected:
bool parse(Context &context, plist::Dictionary const *dict, std::unordered_set<std::string> *seen, bool check) override;

View File

@ -57,7 +57,7 @@ GenerateConfigurationSettings(Filesystem const *filesystem,
std::unique_ptr<plist::Dictionary> settings = nullptr;
if (!isProjectBC) {
for (auto PBC : *project->buildConfigurationList()) {
for (auto const &PBC : project->buildConfigurationList()->buildConfigurations()) {
if (BC.name() == PBC->name()) {
settings = GenerateConfigurationSettings(filesystem, project, *PBC, true);
break;
@ -221,7 +221,7 @@ CompleteDump(Filesystem const *filesystem, PBX::Project::shared_ptr const &proje
}
printf("\t\tConfigurations:\n");
auto DCN = I->buildConfigurationList()->defaultConfigurationName();
for (auto J : *I->buildConfigurationList()) {
for (auto const &J : I->buildConfigurationList()->buildConfigurations()) {
printf("\t\t\t%s%s\n", J->name().c_str(),
J->name() == DCN ? " [Default]" : "");
}
@ -430,7 +430,7 @@ main(int argc, char **argv)
if (project->buildConfigurationList()) {
printf("%4sBuild Configurations:\n", "");
for (auto config : *project->buildConfigurationList()) {
for (auto const &config : project->buildConfigurationList()->buildConfigurations()) {
printf("%8s%s\n", "", config->name().c_str());
}
printf("\n%4sIf no build configuration is specified and -scheme "

View File

@ -93,7 +93,7 @@ Run(Filesystem const *filesystem, Options const &options)
if (project->buildConfigurationList()) {
printf("%4sBuild Configurations:\n", "");
for (auto const &config : *project->buildConfigurationList()) {
for (auto const &config : project->buildConfigurationList()->buildConfigurations()) {
printf("%8s%s\n", "", config->name().c_str());
}
printf("\n%4sIf no build configuration is specified and -scheme is not passed then \"%s\" is used.\n", "", project->buildConfigurationList()->defaultConfigurationName().c_str());

View File

@ -452,11 +452,6 @@ buildAction(
*/
writer.rule(NinjaRuleName(), ninja::Value::Expression("cd $dir && env -i $env $exec && $depexec"));
/*
* Build up a list of all of the inputs to the build, so Ninja can regenerate as necessary.
*/
std::vector<std::string> inputPaths = buildContext.workspaceContext().loadedFilePaths();
/*
* Go over each target and write out Ninja targets for the start and end of each.
* Don't bother topologically sorting the targets now, since Ninja will do that for us.
@ -570,18 +565,13 @@ buildAction(
invocationOutputsValues.push_back(ninja::Value::String(output));
}
writer.build({ ninja::Value::String(targetFinish) }, "phony", { }, { }, invocationOutputsValues);
/*
* Add loaded files for the target configuration to the inputs.
*/
if (auto projectConfigurationFile = targetEnvironment->projectConfigurationFile()) {
inputPaths.push_back(projectConfigurationFile->path());
}
if (auto targetConfigurationFile = targetEnvironment->targetConfigurationFile()) {
inputPaths.push_back(targetConfigurationFile->path());
}
}
/*
* Build up a list of all of the inputs to the build, so Ninja can regenerate as necessary.
*/
std::vector<std::string> inputPaths = buildContext.workspaceContext().loadedFilePaths();
/*
* Add a Ninja rule to regenerate the build.ninja file itself.
*/