blob: 7fe7127fcd84c3b4322d0c0bb78c447839de2a94 [file] [log] [blame]
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmGlobalGNGenerator.h"
#include "cmDocumentationEntry.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmLocalGenerator.h"
#include "cmLocalGNGenerator.h"
#include "cmMakefile.h"
#include "cmState.h"
#include "cmVersion.h"
class cmake;
const char* cmGlobalGNGenerator::GN_BUILD_FILE = "BUILD.gn";
const char* cmGlobalGNGenerator::INDENT = " ";
#if 0
namespace {
class cmGNWriter
{
public:
cmGNWriter(std::ostream& output, std::size_t level = 0);
private:
std::ostream& Output;
std::size_t Level;
};
cmGNWriter::cmGNWriter(std::ostream& output, std::size_t level = 0)
: Output(output)
, Level(level)
{
}
void cmGNWriter::StartScope(std::string const& name)
{
Output << type + "(\"" + name + "\")";
Output << "{";
}
};
#endif
void cmGlobalGNGenerator::Indent(std::ostream& os, int count)
{
for (int i = 0; i < count; ++i) {
os << cmGlobalGNGenerator::INDENT;
}
}
void cmGlobalGNGenerator::WriteComment(std::ostream& os,
const std::string& comment)
{
if (comment.empty()) {
return;
}
std::string::size_type lpos = 0;
std::string::size_type rpos;
while ((rpos = comment.find('\n', lpos)) != std::string::npos) {
os << "# " << comment.substr(lpos, rpos - lpos) << "\n";
lpos = rpos + 1;
}
os << "# " << comment.substr(lpos) << "\n";
}
void cmGlobalGNGenerator::WriteVariable(std::ostream& os,
const std::string& name,
const std::string& value,
const std::string& comment,
int indent)
{
// Make sure we have a name.
if (name.empty()) {
cmSystemTools::Error("No name given for WriteVariable! called "
"with comment: ",
comment.c_str());
return;
}
// Do not add a variable if the value is empty.
std::string val = cmSystemTools::TrimWhitespace(value);
if (val.empty()) {
return;
}
cmGlobalGNGenerator::WriteComment(os, comment);
cmGlobalGNGenerator::Indent(os, indent);
os << name << " = " << val << "\n";
}
void cmGlobalGNGenerator::WriteVariable(std::ostream& os,
const std::string& name,
const std::vector<std::string>& values,
const std::string& comment,
int indent)
{
// Make sure we have a name.
if (name.empty()) {
cmSystemTools::Error("No name given for WriteVariable! called "
"with comment: ",
comment.c_str());
return;
}
// Do not add a variable if there are no values.
if (values.empty()) {
return;
}
cmGlobalGNGenerator::WriteComment(os, comment);
cmGlobalGNGenerator::Indent(os, indent);
os << name << " = [\n";
for (std::vector<std::string>::const_iterator i = values.begin();
i != values.end(); ++i) {
std::string val = cmSystemTools::TrimWhitespace(*i);
cmGlobalGNGenerator::Indent(os, indent + 1);
os << "\"" << val << "\",\n";
}
cmGlobalGNGenerator::Indent(os, indent);
os << "]\n";
}
void cmGlobalGNGenerator::WriteFunction(std::ostream& os,
const std::string& name,
const std::vector<std::string>& args,
const std::string& comment,
int indent)
{
// Make sure we have a name.
if (name.empty()) {
cmSystemTools::Error("No name given for WriteFunction! called "
"with comment: ",
comment.c_str());
return;
}
// Do not add a variable if there are no values.
//if (values.empty()) {
// return;
//}
cmGlobalGNGenerator::WriteComment(os, comment);
cmGlobalGNGenerator::Indent(os, indent);
os << name << "(";
for (std::vector<std::string>::const_iterator i = args.begin();
i != args.end(); ++i) {
std::string val = cmSystemTools::TrimWhitespace(*i);
os << "\"" << val << "\"";
if (i + 1 == args.end())
os << ",";
}
os << ")\n";
}
void cmGlobalGNGenerator::WriteTarget(
std::ostream& os, const std::string& comment, const std::string& type,
const std::string& name, const std::string& output_name,
const std::vector<std::string>& flags,
const std::vector<std::string>& defines,
const std::vector<std::string>& includes,
const std::vector<std::string>& sources,
const std::vector<std::string>& deps,
const std::map<std::string, std::string>& variables,
int cmdLineLimit)
{
cmGlobalGNGenerator::WriteComment(os, comment);
std::string id = type + "(\"" + name + "\")";
std::ostringstream variable_assignments;
cmGlobalGNGenerator::WriteVariable(variable_assignments, "cflags", flags, "", 1);
cmGlobalGNGenerator::WriteVariable(variable_assignments, "defines", defines, "", 1);
cmGlobalGNGenerator::WriteVariable(variable_assignments, "include_dirs", includes, "", 1);
cmGlobalGNGenerator::WriteVariable(variable_assignments, "sources", sources, "", 1);
cmGlobalGNGenerator::WriteVariable(variable_assignments, "deps", deps, "", 1);
for (std::map<std::string, std::string>::const_iterator i = variables.begin();
i != variables.end(); ++i) {
cmGlobalGNGenerator::WriteVariable(variable_assignments, i->first,
i->second, "", 1);
}
std::string assignments = variable_assignments.str();
os << id << " {\n" << assignments << "}\n";
}
cmGlobalGNGenerator::cmGlobalGNGenerator(cmake* cm)
: cmGlobalCommonGenerator(cm)
, BuildFileStream(CM_NULLPTR)
{
this->FindMakeProgramFile = "CMakeGNFindMake.cmake";
}
cmGlobalGNGenerator::~cmGlobalGNGenerator()
{
}
static void WriteDotGN(std::ostream& os)
{
cmGlobalGNGenerator::WriteVariable(os, "buildconfig", "\"//build/BUILDCONFIG.gn\"");
}
static void WriteBuildConfig(std::ostream& os)
{
os << "set_default_toolchain(\"//build:host\")";
}
static void WriteToolchainBuild(std::ostream& os)
{
os << "toolchain(\"host\") {\n";
cmGlobalGNGenerator::WriteVariable(os, "cc", "\"cc\"", "", 1);
cmGlobalGNGenerator::WriteVariable(os, "cxx", "\"c++\"", "", 1);
cmGlobalGNGenerator::WriteVariable(os, "ar", "\"ar\"", "", 1);
cmGlobalGNGenerator::WriteVariable(os, "ld", "cxx", "", 1);
os << " tool(\"cc\") {\n";
cmGlobalGNGenerator::WriteVariable(os, "depfile", "\"{{output}}.d\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "command", "\"$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "depsformat", "\"gcc\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "description", "\"CC {{output}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "outputs", std::vector<std::string>{
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
}, "", 2);
os << " }\n";
os << " tool(\"cxx\") {\n";
cmGlobalGNGenerator::WriteVariable(os, "depfile", "\"{{output}}.d\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "command", "\"$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "depsformat", "\"gcc\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "description", "\"CXX {{output}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "outputs", std::vector<std::string>{
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
}, "", 2);
os << " }\n";
os << " tool(\"alink\") {\n";
cmGlobalGNGenerator::WriteVariable(os, "rspfile", "\"{{output}}.rsp\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "command", "\"rm -f {{output}} && $ar {{arflags}} rcsD {{output}} @\\\"$rspfile\\\"\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "description", "\"AR {{output}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "rspfile_content", "\"{{inputs}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "outputs", std::vector<std::string>{
"{{output_dir}}/{{target_output_name}}{{output_extension}}",
}, "", 2);
cmGlobalGNGenerator::WriteVariable(os, "default_output_dir", "\"{{root_out_dir}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "default_output_extension", "\".a\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "output_prefix", "\"lib\"", "", 2);
os << " }\n";
os << " tool(\"solink\") {\n";
cmGlobalGNGenerator::WriteVariable(os, "outname", "\"{{target_output_name}}{{output_extension}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "outfile", "\"{{output_dir}}/$outname\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "rspfile", "\"$outfile.rsp\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "command", "\"$ld -shared {{ldflags}} -o \\\"$outfile\\\" -Wl,-soname=\\\"$outname\\\" @\\\"$rspfile\\\"\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "description", "\"SOLINK $outfile\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "rspfile_content", "\"-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "outputs", std::vector<std::string>{
"{{output_dir}}/{{target_output_name}}{{output_extension}}",
}, "", 2);
cmGlobalGNGenerator::WriteVariable(os, "default_output_dir", "\"{{root_out_dir}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "default_output_extension", "\".so\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "output_prefix", "\"lib\"", "", 2);
os << " }\n";
os << " tool(\"link\") {\n";
cmGlobalGNGenerator::WriteVariable(os, "outfile", "\"{{output_dir}}/{{target_output_name}}{{output_extension}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "rspfile", "\"$outfile.rsp\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "command", "\"$ld {{ldflags}} -o $outfile -Wl,--start-group @\\\"$rspfile\\\" {{solibs}} -Wl,--end-group {{libs}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "description", "\"LINK $outfile\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "default_output_dir", "\"{{root_out_dir}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "rspfile_content", "\"{{inputs}}\"", "", 2);
cmGlobalGNGenerator::WriteVariable(os, "outputs", std::vector<std::string>{
"{{output_dir}}/{{target_output_name}}{{output_extension}}",
}, "", 2);
os << " }\n";
os << "}\n";
}
// Implemented by:
// cmGlobalUnixMakefileGenerator3
// cmGlobalGhsMultiGenerator
// cmGlobalVisualStudio10Generator
// cmGlobalVisualStudio7Generator
// cmGlobalXCodeGenerator
// Called by:
// cmGlobalGenerator::Build()
void cmGlobalGNGenerator::GenerateBuildCommand(
std::vector<std::string>& makeCommand, const std::string& makeProgram,
const std::string& /*projectName*/, const std::string& projectDir,
const std::string& targetName, const std::string& /*config*/, bool /*fast*/,
bool verbose, std::vector<std::string> const& makeOptions)
{
makeCommand.push_back(this->SelectMakeProgram(makeProgram));
//if (verbose) {
// makeCommand.push_back("-v");
//}
if (!projectDir.empty()) {
cmGeneratedFileStream *dot = new cmGeneratedFileStream(
(projectDir + "/.gn").c_str(), false, this->GetMakefileEncoding());
WriteDotGN(*dot);
delete dot;
cmGeneratedFileStream *buildconfig = new cmGeneratedFileStream(
(projectDir + "/build/BUILDCONFIG.gn").c_str(), false, this->GetMakefileEncoding());
WriteBuildConfig(*buildconfig);
delete buildconfig;
cmGeneratedFileStream *toolchain = new cmGeneratedFileStream(
(projectDir + "/build/BUILD.gn").c_str(), false, this->GetMakefileEncoding());
WriteToolchainBuild(*toolchain);
delete toolchain;
}
makeCommand.insert(makeCommand.end(), makeOptions.begin(),
makeOptions.end());
if (!targetName.empty()) {
if (targetName == "clean") {
makeCommand.push_back("-t");
makeCommand.push_back("clean");
} else {
makeCommand.push_back("gen");
makeCommand.push_back(targetName);
}
}
}
// Non-virtual public methods
std::string cmGlobalGNGenerator::ConvertToGNPath(
const std::string& path) const
{
cmLocalGNGenerator* gn =
static_cast<cmLocalGNGenerator*>(this->LocalGenerators[0]);
return gn->ConvertToRelativePath(
this->LocalGenerators[0]->GetState()->GetBinaryDirectory(), path);
/*return this->NinjaOutputPath(convPath);*/
}
void cmGlobalGNGenerator::AppendTargetDepends(
cmGeneratorTarget const* target, std::vector<std::string>& outputs)
{
if (target->GetType() == cmStateEnums::GLOBAL_TARGET) {
// These depend only on other CMake-provided targets, e.g. "all".
std::set<std::string> const& utils = target->GetUtilities();
for (std::set<std::string>::const_iterator i = utils.begin();
i != utils.end(); ++i) {
std::string d =
target->GetLocalGenerator()->GetCurrentBinaryDirectory() +
std::string("/") + *i;
outputs.push_back(this->ConvertToGNPath(d));
}
} else {
std::vector<std::string> outs;
std::string config =
target->Target->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE");
cmTargetDependSet const& targetDeps = this->GetTargetDirectDepends(target);
for (cmTargetDependSet::const_iterator i = targetDeps.begin();
i != targetDeps.end(); ++i) {
if ((*i)->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
continue;
}
/*outs.push_back(this->ConvertToGNPath(
(*i)->GetFullPath(config, false, false)));*/
outs.push_back(this->ConvertToGNPath((*i)->GetName()));
}
std::sort(outs.begin(), outs.end());
outputs.insert(outputs.end(), outs.begin(), outs.end());
}
}
// Virtual public methods.
cmLocalGenerator* cmGlobalGNGenerator::CreateLocalGenerator(cmMakefile* mf)
{
return new cmLocalGNGenerator(this, mf);
}
codecvt::Encoding cmGlobalGNGenerator::GetMakefileEncoding() const
{
return codecvt::None;
}
void cmGlobalGNGenerator::GetDocumentation(cmDocumentationEntry& entry)
{
entry.Name = cmGlobalGNGenerator::GetActualName();
entry.Brief = "Generates BUILD.gn files.";
}
void cmGlobalGNGenerator::Generate()
{
this->OpenBuildFileStream();
this->cmGlobalGenerator::Generate();
this->CloseBuildFileStream();
}
// Private methods
void cmGlobalGNGenerator::OpenBuildFileStream()
{
// Compute GN's build file path.
std::string buildFilePath =
this->GetCMakeInstance()->GetHomeOutputDirectory();
buildFilePath += "/";
buildFilePath += cmGlobalGNGenerator::GN_BUILD_FILE;
// Get a stream where to generate things.
if (!this->BuildFileStream) {
this->BuildFileStream = new cmGeneratedFileStream(
buildFilePath.c_str(), false, this->GetMakefileEncoding());
if (!this->BuildFileStream) {
// An error message is generated by the constructor if it cannot
// open the file.
return;
}
}
// Write the do not edit header.
this->WriteDisclaimer(*this->BuildFileStream);
}
void cmGlobalGNGenerator::CloseBuildFileStream()
{
if (this->BuildFileStream) {
delete this->BuildFileStream;
this->BuildFileStream = CM_NULLPTR;
} else {
cmSystemTools::Error("Build file stream was not open.");
}
}
// Private virtual overrides
void cmGlobalGNGenerator::WriteDisclaimer(std::ostream& os)
{
os << "# CMAKE generated file: DO NOT EDIT!\n"
<< "# Generated by \"" << this->GetName() << "\""
<< " Generator, CMake Version " << cmVersion::GetMajorVersion() << "."
<< cmVersion::GetMinorVersion() << "\n\n";
}
#if 0
void cmGlobalNinjaGenerator::AppendTargetOutputs(
cmGeneratorTarget const* target, cmNinjaDeps& outputs)
{
std::string configName =
target->Target->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE");
// for frameworks, we want the real name, not smple name
// frameworks always appear versioned, and the build.ninja
// will always attempt to manage symbolic links instead
// of letting cmOSXBundleGenerator do it.
bool realname = target->IsFrameworkOnApple();
switch (target->GetType()) {
case cmStateEnums::EXECUTABLE:
case cmStateEnums::SHARED_LIBRARY:
case cmStateEnums::STATIC_LIBRARY:
case cmStateEnums::MODULE_LIBRARY: {
outputs.push_back(this->ConvertToNinjaPath(
target->GetFullPath(configName, false, realname)));
break;
}
case cmStateEnums::OBJECT_LIBRARY:
case cmStateEnums::GLOBAL_TARGET:
case cmStateEnums::UTILITY: {
std::string path =
target->GetLocalGenerator()->GetCurrentBinaryDirectory() +
std::string("/") + target->GetName();
outputs.push_back(this->ConvertToNinjaPath(path));
break;
}
default:
return;
}
}
#endif