Generate a single project with --ide=xcode Historically we generated multiple project and a workspace to group them together, however the workspace is no longer required as there is just a single project file generated. Remove support for generating a workspace alongside the project, but keep the embedded workspace used to configure the build system. Bug: none Change-Id: If82797f4357232d29318e7b23f12ccbd6cbcf4ef Reviewed-on: https://gn-review.googlesource.com/c/gn/+/9000 Reviewed-by: Brett Wilson <brettw@chromium.org> Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
diff --git a/docs/reference.md b/docs/reference.md index 79b01a2..7896bec 100644 --- a/docs/reference.md +++ b/docs/reference.md
@@ -764,8 +764,8 @@ #### **Xcode Flags** ``` - --workspace=<file_name> - Override defaut workspace file name ("all"). The workspace file is + --xcode-project=<file_name> + Override defaut Xcode project file name ("all"). The project file is written to the root build directory. --ninja-executable=<string>
diff --git a/src/gn/command_gen.cc b/src/gn/command_gen.cc index e9c35bb..8424165 100644 --- a/src/gn/command_gen.cc +++ b/src/gn/command_gen.cc
@@ -48,7 +48,7 @@ const char kSwitchNoDeps[] = "no-deps"; const char kSwitchRootTarget[] = "root-target"; const char kSwitchSln[] = "sln"; -const char kSwitchWorkspace[] = "workspace"; +const char kSwitchXcodeProject[] = "xcode-project"; const char kSwitchJsonFileName[] = "json-file-name"; const char kSwitchJsonIdeScript[] = "json-ide-script"; const char kSwitchJsonIdeScriptArgs[] = "json-ide-script-args"; @@ -58,8 +58,13 @@ // Extracts extra parameters for XcodeWriter from command-line flags. XcodeWriter::Options XcodeWriterOptionsFromCommandLine( const base::CommandLine& command_line) { + std::string project_name = + command_line.GetSwitchValueASCII(kSwitchXcodeProject); + if (project_name.empty()) + project_name = "all"; + return { - command_line.GetSwitchValueASCII(kSwitchWorkspace), + std::move(project_name), command_line.GetSwitchValueASCII(kSwitchRootTarget), command_line.GetSwitchValueASCII(kSwitchNinjaExecutable), command_line.GetSwitchValueASCII(kSwitchNinjaExtraArgs), @@ -403,8 +408,8 @@ Xcode Flags - --workspace=<file_name> - Override defaut workspace file name ("all"). The workspace file is + --xcode-project=<file_name> + Override defaut Xcode project file name ("all"). The project file is written to the root build directory. --ninja-executable=<string>
diff --git a/src/gn/xcode_writer.cc b/src/gn/xcode_writer.cc index 206850f..c1a8538 100644 --- a/src/gn/xcode_writer.cc +++ b/src/gn/xcode_writer.cc
@@ -406,105 +406,91 @@ } // namespace -class XcodeProject; - -// Xcode uses workspace to: -// - group multiple projects together -// - configure settings shared by all targets (like build setting). -// -// The class XcodeBaseWorkspace is used to share code to generate the two -// types of workspace. The only difference between them is where they are -// located (either besides the .xcodeproj or inside them) and how the -// projects are references (either by path or by referencing the containing -// project). -// -// As the generator only creates a single project, the standalone workspace -// is not technically required, but developers are used to opening it instead -// opening the project directly. -// -// The embedded workspace is not needed if developer open the standalone -// workspace (as the settings from the standalone workspace will be used -// in that case). However, there have been multiple occurrences of developer -// encountering tricky build failure because they opened the project directly. -// -// Having the two kind of workspace is a temporary solution to avoid breaking -// workflow of developer used to opening the standalone workspace while still -// ensuring that developers opening the project directly have an environment -// that is working. -class XcodeBaseWorkspace { +// Class representing the workspace embedded in an xcodeproj file used to +// configure the build settings shared by all targets in the project (used +// to configure the build system to "Legacy build system"). +class XcodeWorkspace { public: - XcodeBaseWorkspace(const BuildSettings* settings); - virtual ~XcodeBaseWorkspace(); + XcodeWorkspace(const BuildSettings* settings); + ~XcodeWorkspace(); - XcodeBaseWorkspace(const XcodeBaseWorkspace&) = delete; - XcodeBaseWorkspace& operator=(const XcodeBaseWorkspace&) = delete; + XcodeWorkspace(const XcodeWorkspace&) = delete; + XcodeWorkspace& operator=(const XcodeWorkspace&) = delete; // Generates the .xcworkspace files to disk. bool WriteWorkspace(const std::string& name, Err* err) const; - protected: - const BuildSettings* build_settings() const { return build_settings_; } - private: - using FileContentGenerator = - void (XcodeBaseWorkspace::*)(std::ostream&) const; + // Writes the workspace data file. + bool WriteWorkspaceDataFile(const std::string& name, Err* err) const; - // Helper function to write workspace file. - bool WriteWorkspaceFile(const std::string& filename, - FileContentGenerator file_content_generator, - Err* err) const; - - // Writes the $name.xcworkspace/contents.xcworkspacedata file to |out|. - virtual void WriteWorkspaceDataFileContent(std::ostream& out) const = 0; - - // Writes the $name.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings - // file to |out|. - void WriteSettingsFileContent(std::ostream& out) const; + // Writes the settings file. + bool WriteSettingsFile(const std::string& name, Err* err) const; const BuildSettings* build_settings_ = nullptr; }; -// Class corresponding to the generated workspace contained in a project. -class XcodeInnerWorkspace : public XcodeBaseWorkspace { - public: - XcodeInnerWorkspace(const BuildSettings* settings); - ~XcodeInnerWorkspace() override; +XcodeWorkspace::XcodeWorkspace(const BuildSettings* build_settings) + : build_settings_(build_settings) {} - private: - // XcodeBaseWorkspace implementation. - void WriteWorkspaceDataFileContent(std::ostream& out) const override; -}; +XcodeWorkspace::~XcodeWorkspace() = default; -// Sub-class XcodeOuterWorkspace corresponds to a standalone workspace that -// points to .xcodeproj located next to the .xcworkspace. It can refer to -// multiple project (in the generator it owns the projects). -class XcodeOuterWorkspace : public XcodeBaseWorkspace { - public: - XcodeOuterWorkspace(const BuildSettings* settings, - XcodeWriter::Options options); - ~XcodeOuterWorkspace() override; +bool XcodeWorkspace::WriteWorkspace(const std::string& name, Err* err) const { + return WriteWorkspaceDataFile(name, err) && WriteSettingsFile(name, err); +} - // Adds a project to the workspace. - XcodeProject* CreateProject(const std::string& name); +bool XcodeWorkspace::WriteWorkspaceDataFile(const std::string& name, + Err* err) const { + const SourceFile source_file = + build_settings_->build_dir().ResolveRelativeFile( + Value(nullptr, name + "/contents.xcworkspacedata"), err); + if (source_file.is_null()) + return false; - // Returns the name of the workspace. - const std::string& Name() const; + std::stringstream out; + out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<Workspace\n" + << " version = \"1.0\">\n" + << " <FileRef\n" + << " location = \"self:\">\n" + << " </FileRef>\n" + << "</Workspace>\n"; - private: - // XcodeBaseWorkspace implementation - void WriteWorkspaceDataFileContent(std::ostream& out) const override; + return WriteFileIfChanged(build_settings_->GetFullPath(source_file), + out.str(), err); +} - XcodeWriter::Options options_; - std::map<std::string, std::unique_ptr<XcodeProject>> projects_; -}; +bool XcodeWorkspace::WriteSettingsFile(const std::string& name, + Err* err) const { + const SourceFile source_file = + build_settings_->build_dir().ResolveRelativeFile( + Value(nullptr, name + "/xcshareddata/WorkspaceSettings.xcsettings"), + err); + if (source_file.is_null()) + return false; -// Sub-class XcodeInnerWorkspace corresponds to a workspace that is embedded -// inside an .xcodeproj. It only refer to the surrounding project via :self. + std::stringstream out; + out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " + << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + << "<plist version=\"1.0\">\n" + << "<dict>\n" + << "\t<key>BuildSystemType</key>\n" + << "\t<string>Original</string>\n" + << "</dict>\n" + << "</plist>\n"; + + return WriteFileIfChanged(build_settings_->GetFullPath(source_file), + out.str(), err); +} + +// Class responsible for constructing and writing the .xcodeproj from the +// targets known to gn. It currently requires using the "Legacy build system" +// so it will embed an .xcworkspace file to force the setting. class XcodeProject { public: XcodeProject(const BuildSettings* build_settings, - XcodeWriter::Options options, - const std::string& name); + XcodeWriter::Options options); ~XcodeProject(); // Recursively finds "source" files from |builder| and adds them to the @@ -568,11 +554,10 @@ }; XcodeProject::XcodeProject(const BuildSettings* build_settings, - XcodeWriter::Options options, - const std::string& name) + XcodeWriter::Options options) : build_settings_(build_settings), options_(options), - project_(name, + project_(options.project_name, ConfigNameFromBuildSettings(build_settings), SourcePathFromBuildSettings(build_settings), ProjectAttributesFromBuildSettings(build_settings)) {} @@ -817,9 +802,9 @@ return false; } - XcodeInnerWorkspace inner_workspace(build_settings_); - return inner_workspace.WriteWorkspace(project_.Name() + ".xcodeproj/project", - err); + XcodeWorkspace workspace(build_settings_); + return workspace.WriteWorkspace( + project_.Name() + ".xcodeproj/project.xcworkspace", err); } std::optional<std::vector<const Target*>> XcodeProject::GetTargetsFromBuilder( @@ -950,126 +935,22 @@ << "}\n"; } -XcodeBaseWorkspace::XcodeBaseWorkspace(const BuildSettings* build_settings) - : build_settings_(build_settings) {} - -XcodeBaseWorkspace::~XcodeBaseWorkspace() = default; - -bool XcodeBaseWorkspace::WriteWorkspace(const std::string& name, - Err* err) const { - if (!WriteWorkspaceFile(name + ".xcworkspace/contents.xcworkspacedata", - &XcodeBaseWorkspace::WriteWorkspaceDataFileContent, - err)) { - return false; - } - - if (!WriteWorkspaceFile( - name + ".xcworkspace/xcshareddata/WorkspaceSettings.xcsettings", - &XcodeBaseWorkspace::WriteSettingsFileContent, err)) { - return false; - } - - return true; -} - -bool XcodeBaseWorkspace::WriteWorkspaceFile( - const std::string& filename, - FileContentGenerator file_content_generator, - Err* err) const { - const SourceFile source_file = - build_settings_->build_dir().ResolveRelativeFile(Value(nullptr, filename), - err); - if (source_file.is_null()) - return false; - - std::stringstream string_out; - (this->*file_content_generator)(string_out); - - return WriteFileIfChanged(build_settings_->GetFullPath(source_file), - string_out.str(), err); -} - -void XcodeBaseWorkspace::WriteSettingsFileContent(std::ostream& out) const { - out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " - << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" - << "<plist version=\"1.0\">\n" - << "<dict>\n" - << "\t<key>BuildSystemType</key>\n" - << "\t<string>Original</string>\n" - << "</dict>\n" - << "</plist>\n"; -} - -XcodeInnerWorkspace::XcodeInnerWorkspace(const BuildSettings* build_settings) - : XcodeBaseWorkspace(build_settings) {} - -XcodeInnerWorkspace::~XcodeInnerWorkspace() = default; - -void XcodeInnerWorkspace::WriteWorkspaceDataFileContent( - std::ostream& out) const { - out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - << "<Workspace\n" - << " version = \"1.0\">\n" - << " <FileRef\n" - << " location = \"self:\">\n" - << " </FileRef>\n" - << "</Workspace>\n"; -} - -XcodeOuterWorkspace::XcodeOuterWorkspace(const BuildSettings* build_settings, - XcodeWriter::Options options) - : XcodeBaseWorkspace(build_settings), options_(options) {} - -XcodeOuterWorkspace::~XcodeOuterWorkspace() = default; - -XcodeProject* XcodeOuterWorkspace::CreateProject(const std::string& name) { - DCHECK(!base::ContainsKey(projects_, name)); - projects_.insert(std::make_pair( - name, std::make_unique<XcodeProject>(build_settings(), options_, name))); - auto iter = projects_.find(name); - DCHECK(iter != projects_.end()); - DCHECK(iter->second); - return iter->second.get(); -} - -const std::string& XcodeOuterWorkspace::Name() const { - static std::string all("all"); - return !options_.workspace_name.empty() ? options_.workspace_name : all; -} - -void XcodeOuterWorkspace::WriteWorkspaceDataFileContent( - std::ostream& out) const { - out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - << "<Workspace version = \"1.0\">\n"; - for (const auto& pair : projects_) { - out << " <FileRef location = \"group:" << pair.first - << ".xcodeproj\"></FileRef>\n"; - } - out << "</Workspace>\n"; -} - // static bool XcodeWriter::RunAndWriteFiles(const BuildSettings* build_settings, const Builder& builder, Options options, Err* err) { - XcodeOuterWorkspace workspace(build_settings, options); - - XcodeProject* products = workspace.CreateProject("products"); - if (!products->AddSourcesFromBuilder(builder, err)) + XcodeProject project(build_settings, options); + if (!project.AddSourcesFromBuilder(builder, err)) return false; - if (!products->AddTargetsFromBuilder(builder, err)) + if (!project.AddTargetsFromBuilder(builder, err)) return false; - if (!products->AssignIds(err)) + if (!project.AssignIds(err)) return false; - if (!products->WriteFile(err)) - return false; - - if (!workspace.WriteWorkspace(workspace.Name(), err)) + if (!project.WriteFile(err)) return false; return true;
diff --git a/src/gn/xcode_writer.h b/src/gn/xcode_writer.h index 69d4903..62f6ab5 100644 --- a/src/gn/xcode_writer.h +++ b/src/gn/xcode_writer.h
@@ -22,8 +22,8 @@ public: // Controls some parameters and behaviour of the RunAndWriteFiles(). struct Options { - // Name of the generated workspace file. Defaults to "all" is empty. - std::string workspace_name; + // Name of the generated project file. Defaults to "all" is empty. + std::string project_name; // Name of the ninja target to use for the "All" target in the generated // project. If empty, no target will be passed to ninja which will thus