From 8d3dad9a76591ae0426335d039b8aaacb95862cd Mon Sep 17 00:00:00 2001 From: Fred Baksik Date: Mon, 8 Apr 2019 09:55:34 -0400 Subject: GHS: Support add_custom_command( OUTPUT ) signature -- add new file type to run a shell script -- gbuild does not compute interfile dependencies like other build tools. Therefore calculate the required build order of custom commands and list all of them in the CMake Rules subproject. --- Source/cmGhsMultiTargetGenerator.cxx | 174 ++++++++++++++++++++++++++++++----- Source/cmGhsMultiTargetGenerator.h | 8 ++ Source/cmGlobalGhsMultiGenerator.cxx | 61 +++++++++++- Source/cmGlobalGhsMultiGenerator.h | 3 +- 4 files changed, 219 insertions(+), 27 deletions(-) diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index 9f9ef18..9d0d195 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -572,31 +572,82 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) *fout << "{comment} Others" << std::endl; } - /* output rule for each source file */ - for (const cmSourceFile* si : groupFiles[sg]) { - - // Convert filename to native system - // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on - // windows when opening some files from the search window. - std::string fname(si->GetFullPath()); - cmSystemTools::ConvertToOutputSlashes(fname); - *fout << fname << std::endl; - - if ("ld" != si->GetExtension() && "int" != si->GetExtension() && - "bsp" != si->GetExtension()) { - WriteObjectLangOverride(*fout, si); - } + if (sg != "CMake Rules") { + /* output rule for each source file */ + for (const cmSourceFile* si : groupFiles[sg]) { + + // Convert filename to native system + // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on + // windows when opening some files from the search window. + std::string fname(si->GetFullPath()); + cmSystemTools::ConvertToOutputSlashes(fname); + + /* Comment out any custom command dependencies to prevent from + * being considered part of the build. + */ + std::string comment; + if (si->GetCustomCommand()) { + comment = "{comment} "; + } + *fout << comment << fname << std::endl; - this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); - this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); - this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); + if ("ld" != si->GetExtension() && "int" != si->GetExtension() && + "bsp" != si->GetExtension()) { + WriteObjectLangOverride(*fout, si); + } - /* to avoid clutter in the gui only print out the objectName if it has - * been renamed */ - std::string objectName = this->GeneratorTarget->GetObjectName(si); - if (!objectName.empty() && - this->GeneratorTarget->HasExplicitObjectName(si)) { - *fout << " -o " << objectName << std::endl; + this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); + this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); + this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); + + /* to avoid clutter in the gui only print out the objectName if it has + * been renamed */ + std::string objectName = this->GeneratorTarget->GetObjectName(si); + if (!objectName.empty() && + this->GeneratorTarget->HasExplicitObjectName(si)) { + *fout << " -o " << objectName << std::endl; + } + } + } else { + std::vector customCommands; + if (ComputeCustomCommandOrder(customCommands)) { + std::string message = "The custom commands for target [" + + this->GeneratorTarget->GetName() + "] had a cycle.\n"; + cmSystemTools::Error(message); + } else { + /* Custom targets do not have a dependency on SOURCES files. + * Therefore the dependency list may include SOURCES files after the + * custom target. Because nothing can depend on the custom target just + * move it to the last item. + */ + for (auto sf = customCommands.begin(); sf != customCommands.end(); + ++sf) { + if (((*sf)->GetLocation()).GetName() == this->Name + ".rule") { + std::rotate(sf, sf + 1, customCommands.end()); + break; + } + } + int cmdcount = 0; + for (auto& sf : customCommands) { + const cmCustomCommand* cc = sf->GetCustomCommand(); + cmCustomCommandGenerator ccg(*cc, this->ConfigName, + this->LocalGenerator); + + // Open the filestream for this custom command + std::string fname = + this->LocalGenerator->GetCurrentBinaryDirectory(); + fname += "/" + + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + fname += "/" + this->Name + "_cc"; + fname += std::to_string(cmdcount++) + "_"; + fname += (sf->GetLocation()).GetName(); + fname += this->CmdWindowsShell ? ".bat" : ".sh"; + cmGeneratedFileStream f(fname.c_str()); + f.SetCopyIfDifferent(true); + this->WriteCustomCommandsHelper(f, ccg); + f.Close(); + this->WriteCustomCommandLine(*fout, fname, ccg); + } } } } @@ -606,6 +657,33 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) } } +void cmGhsMultiTargetGenerator::WriteCustomCommandLine( + std::ostream& fout, std::string& fname, cmCustomCommandGenerator const& ccg) +{ + /* NOTE: Customization Files are not well documented. Testing showed + * that ":outputName=file" can only be used once per script. The + * script will only run if ":outputName=file" is missing or just run + * once if ":outputName=file" is not specified. If there are + * multiple outputs then the script needs to be listed multiple times + * for each output. Otherwise it won't rerun the script if one of + * the outputs is manually deleted. + */ + bool specifyExtra = true; + for (auto& out : ccg.GetOutputs()) { + fout << fname << std::endl; + fout << " :outputName=\"" << out << "\"" << std::endl; + if (specifyExtra) { + for (auto& byp : ccg.GetByproducts()) { + fout << " :extraOutputFile=\"" << byp << "\"" << std::endl; + } + for (auto& dep : ccg.GetDepends()) { + fout << " :depends=\"" << dep << "\"" << std::endl; + } + specifyExtra = false; + } + } +} + void cmGhsMultiTargetGenerator::WriteObjectLangOverride( std::ostream& fout, const cmSourceFile* sourceFile) { @@ -643,7 +721,7 @@ void cmGhsMultiTargetGenerator::WriteReferences(std::ostream& fout) fout << " "; GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fout); - // Tell the global generator that a refernce project needs to be created + // Tell the global generator that a reference project needs to be created t->Target->SetProperty("GHS_REFERENCE_PROJECT", "ON"); } } @@ -664,3 +742,51 @@ bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp() } return false; } + +bool cmGhsMultiTargetGenerator::ComputeCustomCommandOrder( + std::vector& order) +{ + std::set temp; + std::set perm; + + // Collect all custom commands for this target + std::vector customCommands; + this->GeneratorTarget->GetCustomCommands(customCommands, this->ConfigName); + + for (cmSourceFile const* si : customCommands) { + bool r = VisitCustomCommand(temp, perm, order, si); + if (r) { + return r; + } + } + return false; +} + +bool cmGhsMultiTargetGenerator::VisitCustomCommand( + std::set& temp, std::set& perm, + std::vector& order, cmSourceFile const* si) +{ + /* check if permanent mark is set*/ + if (perm.find(si) == perm.end()) { + /* set temporary mark; check if revisit*/ + if (temp.insert(si).second) { + for (auto& di : si->GetCustomCommand()->GetDepends()) { + cmSourceFile const* sf = this->GeneratorTarget->GetLocalGenerator() + ->GetMakefile() + ->GetSourceFileWithOutput(di); + /* if sf exists then visit */ + if (sf && this->VisitCustomCommand(temp, perm, order, sf)) { + return true; + } + } + /* mark as complete; insert into beginning of list*/ + perm.insert(si); + order.push_back(si); + return false; + } + /* revisiting item - not a DAG */ + return true; + } + /* already complete */ + return false; +} diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h index fa251b0..3ba3884 100644 --- a/Source/cmGhsMultiTargetGenerator.h +++ b/Source/cmGhsMultiTargetGenerator.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -54,6 +55,13 @@ private: std::string const& name, std::string const& cmd); void WriteCustomCommandsHelper(std::ostream& fout, cmCustomCommandGenerator const& ccg); + void WriteCustomCommandLine(std::ostream& fout, std::string& fname, + cmCustomCommandGenerator const& ccg); + bool ComputeCustomCommandOrder(std::vector& order); + bool VisitCustomCommand(std::set& temp, + std::set& perm, + std::vector& order, + cmSourceFile const* sf); void WriteSources(std::ostream& fout_proj); void WriteSourceProperty(std::ostream& fout, const cmSourceFile* sf, std::string const& propName, diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index 9f361f6..a2138d1 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -265,6 +265,49 @@ void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout) << std::endl; } +void cmGlobalGhsMultiGenerator::WriteCustomRuleBOD(std::ostream& fout) +{ + fout << "Commands {\n" + " Custom_Rule_Command {\n" + " name = \"Custom Rule Command\"\n" + " exec = \""; +#ifdef _WIN32 + fout << "cmd.exe"; +#else + fout << "/bin/sh"; +#endif + fout << "\"\n" + " options = {\"SpecialOptions\"}\n" + " }\n" + "}\n"; + + fout << "\n\n"; + fout << "FileTypes {\n" + " CmakeRule {\n" + " name = \"Custom Rule\"\n" + " action = \"&Run\"\n" + " extensions = {\""; +#ifdef _WIN32 + fout << "bat"; +#else + fout << "sh"; +#endif + fout << "\"}\n" + " grepable = false\n" + " command = \"Custom Rule Command\"\n" + " commandLine = \"$COMMAND "; +#ifdef _WIN32 + fout << "/c"; +#endif + fout << " $INPUTFILE\"\n" + " progress = \"Processing Custom Rule\"\n" + " promoteToFirstPass = true\n" + " outputType = \"None\"\n" + " color = \"#800080\"\n" + " }\n" + "}\n"; +} + void cmGlobalGhsMultiGenerator::WriteTopLevelProject( std::ostream& fout, cmLocalGenerator* root, std::vector& generators) @@ -272,7 +315,7 @@ void cmGlobalGhsMultiGenerator::WriteTopLevelProject( WriteFileHeader(fout); this->WriteMacros(fout); - this->WriteHighLevelDirectives(fout); + this->WriteHighLevelDirectives(root, fout); GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout); fout << "# Top Level Project File" << std::endl; @@ -363,6 +406,8 @@ void cmGlobalGhsMultiGenerator::WriteSubProjects( void cmGlobalGhsMultiGenerator::Generate() { + std::string fname; + // first do the superclass method this->cmGlobalGenerator::Generate(); @@ -370,6 +415,15 @@ void cmGlobalGhsMultiGenerator::Generate() for (auto& it : this->ProjectMap) { this->OutputTopLevelProject(it.second[0], it.second); } + + // create custom rule BOD file + fname = this->GetCMakeInstance()->GetHomeOutputDirectory() + + "/CMakeFiles/custom_rule.bod"; + cmGeneratedFileStream frule(fname); + frule.SetCopyIfDifferent(true); + this->WriteFileHeader(frule); + this->WriteCustomRuleBOD(frule); + frule.Close(); } void cmGlobalGhsMultiGenerator::OutputTopLevelProject( @@ -465,7 +519,8 @@ void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout) } } -void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout) +void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives( + cmLocalGenerator* root, std::ostream& fout) { /* set primary target */ std::string tgt; @@ -486,6 +541,8 @@ void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout) } fout << "primaryTarget=" << tgt << std::endl; + fout << "customization=" << root->GetBinaryDirectory() + << "/CMakeFiles/custom_rule.bod" << std::endl; char const* const customization = this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION"); diff --git a/Source/cmGlobalGhsMultiGenerator.h b/Source/cmGlobalGhsMultiGenerator.h index 1aeb1dc..1dd951a 100644 --- a/Source/cmGlobalGhsMultiGenerator.h +++ b/Source/cmGlobalGhsMultiGenerator.h @@ -114,9 +114,10 @@ private: void WriteTopLevelProject(std::ostream& fout, cmLocalGenerator* root, std::vector& generators); void WriteMacros(std::ostream& fout); - void WriteHighLevelDirectives(std::ostream& fout); + void WriteHighLevelDirectives(cmLocalGenerator* root, std::ostream& fout); void WriteSubProjects(std::ostream& fout, cmLocalGenerator* root, std::vector& generators); + void WriteCustomRuleBOD(std::ostream& fout); std::string trimQuotes(std::string const& str); -- cgit v0.12