Merge topic 'ninja-output-path-prefix'
eb076692
Tests: Select RunCMake.Ninja test cases based on ninja version8a862a4d
Ninja: Support embedding of CMake as subninja project038e7716
Ninja: Pass all build paths through a central method7c26a6a2
Ninja: Fix path to soname-d target fileac3cdd9a
Ninja: Convert object file names to ninja paths earlierd4381cb1
Ninja: Convert link library file names like all other output paths0397c92a
Ninja: Pre-compute "CMakeCache.txt" build target name3b3ecdfa
Ninja: Pre-compute "all" build target name5ca72750
Ninja: Simplify generation of custom target logical path
This commit is contained in:
commit
e0da6c3b56
|
@ -281,6 +281,7 @@ Variables that Control the Build
|
||||||
/variable/CMAKE_MAP_IMPORTED_CONFIG_CONFIG
|
/variable/CMAKE_MAP_IMPORTED_CONFIG_CONFIG
|
||||||
/variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG
|
/variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG
|
||||||
/variable/CMAKE_MODULE_LINKER_FLAGS
|
/variable/CMAKE_MODULE_LINKER_FLAGS
|
||||||
|
/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX
|
||||||
/variable/CMAKE_NO_BUILTIN_CHRPATH
|
/variable/CMAKE_NO_BUILTIN_CHRPATH
|
||||||
/variable/CMAKE_NO_SYSTEM_FROM_IMPORTED
|
/variable/CMAKE_NO_SYSTEM_FROM_IMPORTED
|
||||||
/variable/CMAKE_OSX_ARCHITECTURES
|
/variable/CMAKE_OSX_ARCHITECTURES
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
ninja-output-path-prefix
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
* The :generator:`Ninja` generator learned to read a new
|
||||||
|
:variable:`CMAKE_NINJA_OUTPUT_PATH_PREFIX` variable to configure
|
||||||
|
the generated ``build.ninja`` file for use as a ``subninja``.
|
|
@ -0,0 +1,27 @@
|
||||||
|
CMAKE_NINJA_OUTPUT_PATH_PREFIX
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Set output files path prefix for the :generator:`Ninja` generator.
|
||||||
|
|
||||||
|
Every output files listed in the generated ``build.ninja`` will be
|
||||||
|
prefixed by the contents of this variable (a trailing slash is
|
||||||
|
appended if missing). This is useful when the generated ninja file is
|
||||||
|
meant to be embedded as a ``subninja`` file into a *super* ninja
|
||||||
|
project. For example, a ninja build file generated with a command
|
||||||
|
like::
|
||||||
|
|
||||||
|
cd top-build-dir/sub &&
|
||||||
|
cmake -G Ninja -DCMAKE_NINJA_OUTPUT_PATH_PREFIX=sub/ path/to/source
|
||||||
|
|
||||||
|
can be embedded in ``top-build-dir/build.ninja`` with a directive like
|
||||||
|
this::
|
||||||
|
|
||||||
|
subninja sub/build.ninja
|
||||||
|
|
||||||
|
The ``auto-regeneration`` rule in ``top-build-dir/build.ninja`` must have an
|
||||||
|
order-only dependency on ``sub/build.ninja``.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
When ``CMAKE_NINJA_OUTPUT_PATH_PREFIX`` is set, the project generated
|
||||||
|
by CMake cannot be used as a standalone project. No default targets
|
||||||
|
are specified.
|
|
@ -379,4 +379,19 @@ std::reverse_iterator<Iter> cmMakeReverseIterator(Iter it)
|
||||||
return std::reverse_iterator<Iter>(it);
|
return std::reverse_iterator<Iter>(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool cmHasSuffix(const std::string& str, const std::string& suffix)
|
||||||
|
{
|
||||||
|
if (str.size() < suffix.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void cmStripSuffixIfExists(std::string& str, const std::string& suffix)
|
||||||
|
{
|
||||||
|
if (cmHasSuffix(str, suffix)) {
|
||||||
|
str.resize(str.size() - suffix.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -488,6 +488,10 @@ void cmGlobalNinjaGenerator::Generate()
|
||||||
this->OpenBuildFileStream();
|
this->OpenBuildFileStream();
|
||||||
this->OpenRulesFileStream();
|
this->OpenRulesFileStream();
|
||||||
|
|
||||||
|
this->InitOutputPathPrefix();
|
||||||
|
this->TargetAll = this->NinjaOutputPath("all");
|
||||||
|
this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt");
|
||||||
|
|
||||||
this->PolicyCMP0058 =
|
this->PolicyCMP0058 =
|
||||||
this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus(
|
this->LocalGenerators[0]->GetMakefile()->GetPolicyStatus(
|
||||||
cmPolicies::CMP0058);
|
cmPolicies::CMP0058);
|
||||||
|
@ -714,11 +718,29 @@ void cmGlobalNinjaGenerator::CloseRulesFileStream()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void EnsureTrailingSlash(std::string& path)
|
||||||
|
{
|
||||||
|
if (path.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string::value_type last = path[path.size() - 1];
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (last != '\\') {
|
||||||
|
path += '\\';
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (last != '/') {
|
||||||
|
path += '/';
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
std::string cmGlobalNinjaGenerator::ConvertToNinjaPath(const std::string& path)
|
std::string cmGlobalNinjaGenerator::ConvertToNinjaPath(const std::string& path)
|
||||||
{
|
{
|
||||||
cmLocalNinjaGenerator* ng =
|
cmLocalNinjaGenerator* ng =
|
||||||
static_cast<cmLocalNinjaGenerator*>(this->LocalGenerators[0]);
|
static_cast<cmLocalNinjaGenerator*>(this->LocalGenerators[0]);
|
||||||
std::string convPath = ng->Convert(path, cmOutputConverter::HOME_OUTPUT);
|
std::string convPath = ng->Convert(path, cmOutputConverter::HOME_OUTPUT);
|
||||||
|
convPath = this->NinjaOutputPath(convPath);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
cmSystemTools::ReplaceString(convPath, "/", "\\");
|
cmSystemTools::ReplaceString(convPath, "/", "\\");
|
||||||
#endif
|
#endif
|
||||||
|
@ -731,6 +753,7 @@ std::string cmGlobalNinjaGenerator::ConvertToNinjaFolderRule(
|
||||||
cmLocalNinjaGenerator* ng =
|
cmLocalNinjaGenerator* ng =
|
||||||
static_cast<cmLocalNinjaGenerator*>(this->LocalGenerators[0]);
|
static_cast<cmLocalNinjaGenerator*>(this->LocalGenerators[0]);
|
||||||
std::string convPath = ng->Convert(path + "/all", cmOutputConverter::HOME);
|
std::string convPath = ng->Convert(path + "/all", cmOutputConverter::HOME);
|
||||||
|
convPath = this->NinjaOutputPath(convPath);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
cmSystemTools::ReplaceString(convPath, "/", "\\");
|
cmSystemTools::ReplaceString(convPath, "/", "\\");
|
||||||
#endif
|
#endif
|
||||||
|
@ -836,22 +859,17 @@ void cmGlobalNinjaGenerator::AppendTargetOutputs(
|
||||||
}
|
}
|
||||||
case cmState::OBJECT_LIBRARY:
|
case cmState::OBJECT_LIBRARY:
|
||||||
case cmState::UTILITY: {
|
case cmState::UTILITY: {
|
||||||
std::string path = this->ConvertToNinjaPath(
|
std::string path =
|
||||||
target->GetLocalGenerator()->GetCurrentBinaryDirectory());
|
target->GetLocalGenerator()->GetCurrentBinaryDirectory() +
|
||||||
if (path.empty() || path == ".")
|
std::string("/") + target->GetName();
|
||||||
outputs.push_back(target->GetName());
|
outputs.push_back(this->ConvertToNinjaPath(path));
|
||||||
else {
|
|
||||||
path += "/";
|
|
||||||
path += target->GetName();
|
|
||||||
outputs.push_back(path);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case cmState::GLOBAL_TARGET:
|
case cmState::GLOBAL_TARGET:
|
||||||
// Always use the target in HOME instead of an unused duplicate in a
|
// Always use the target in HOME instead of an unused duplicate in a
|
||||||
// subdirectory.
|
// subdirectory.
|
||||||
outputs.push_back(target->GetName());
|
outputs.push_back(this->NinjaOutputPath(target->GetName()));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -885,6 +903,7 @@ void cmGlobalNinjaGenerator::AppendTargetDepends(
|
||||||
void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias,
|
void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias,
|
||||||
cmGeneratorTarget* target)
|
cmGeneratorTarget* target)
|
||||||
{
|
{
|
||||||
|
std::string buildAlias = this->NinjaOutputPath(alias);
|
||||||
cmNinjaDeps outputs;
|
cmNinjaDeps outputs;
|
||||||
this->AppendTargetOutputs(target, outputs);
|
this->AppendTargetOutputs(target, outputs);
|
||||||
// Mark the target's outputs as ambiguous to ensure that no other target uses
|
// Mark the target's outputs as ambiguous to ensure that no other target uses
|
||||||
|
@ -895,7 +914,7 @@ void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias,
|
||||||
// Insert the alias into the map. If the alias was already present in the
|
// Insert the alias into the map. If the alias was already present in the
|
||||||
// map and referred to another target, mark it as ambiguous.
|
// map and referred to another target, mark it as ambiguous.
|
||||||
std::pair<TargetAliasMap::iterator, bool> newAlias =
|
std::pair<TargetAliasMap::iterator, bool> newAlias =
|
||||||
TargetAliases.insert(std::make_pair(alias, target));
|
TargetAliases.insert(std::make_pair(buildAlias, target));
|
||||||
if (newAlias.second && newAlias.first->second != target)
|
if (newAlias.second && newAlias.first->second != target)
|
||||||
newAlias.first->second = 0;
|
newAlias.first->second = 0;
|
||||||
}
|
}
|
||||||
|
@ -1043,7 +1062,7 @@ void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
knownDependencies.insert("CMakeCache.txt");
|
knownDependencies.insert(this->CMakeCacheFile);
|
||||||
|
|
||||||
for (TargetAliasMap::const_iterator i = this->TargetAliases.begin();
|
for (TargetAliasMap::const_iterator i = this->TargetAliases.begin();
|
||||||
i != this->TargetAliases.end(); ++i) {
|
i != this->TargetAliases.end(); ++i) {
|
||||||
|
@ -1064,7 +1083,7 @@ void cmGlobalNinjaGenerator::WriteUnknownExplicitDependencies(std::ostream& os)
|
||||||
// should all match now.
|
// should all match now.
|
||||||
|
|
||||||
std::vector<std::string> unknownExplicitDepends;
|
std::vector<std::string> unknownExplicitDepends;
|
||||||
this->CombinedCustomCommandExplicitDependencies.erase("all");
|
this->CombinedCustomCommandExplicitDependencies.erase(this->TargetAll);
|
||||||
|
|
||||||
std::set_difference(this->CombinedCustomCommandExplicitDependencies.begin(),
|
std::set_difference(this->CombinedCustomCommandExplicitDependencies.begin(),
|
||||||
this->CombinedCustomCommandExplicitDependencies.end(),
|
this->CombinedCustomCommandExplicitDependencies.end(),
|
||||||
|
@ -1130,13 +1149,15 @@ void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
|
||||||
void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
|
void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
|
||||||
{
|
{
|
||||||
cmNinjaDeps outputs;
|
cmNinjaDeps outputs;
|
||||||
outputs.push_back("all");
|
outputs.push_back(this->TargetAll);
|
||||||
|
|
||||||
this->WritePhonyBuild(os, "The main all target.", outputs,
|
this->WritePhonyBuild(os, "The main all target.", outputs,
|
||||||
this->AllDependencies);
|
this->AllDependencies);
|
||||||
|
|
||||||
cmGlobalNinjaGenerator::WriteDefault(os, outputs,
|
if (!this->HasOutputPathPrefix()) {
|
||||||
"Make the all target the default.");
|
cmGlobalNinjaGenerator::WriteDefault(os, outputs,
|
||||||
|
"Make the all target the default.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
|
void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
|
||||||
|
@ -1171,7 +1192,7 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
|
||||||
implicitDeps.push_back(this->ConvertToNinjaPath(*fi));
|
implicitDeps.push_back(this->ConvertToNinjaPath(*fi));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
implicitDeps.push_back("CMakeCache.txt");
|
implicitDeps.push_back(this->CMakeCacheFile);
|
||||||
|
|
||||||
std::sort(implicitDeps.begin(), implicitDeps.end());
|
std::sort(implicitDeps.begin(), implicitDeps.end());
|
||||||
implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()),
|
implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()),
|
||||||
|
@ -1184,9 +1205,10 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
|
||||||
variables["pool"] = "console";
|
variables["pool"] = "console";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string const ninjaBuildFile = this->NinjaOutputPath(NINJA_BUILD_FILE);
|
||||||
this->WriteBuild(os, "Re-run CMake if any of its inputs changed.",
|
this->WriteBuild(os, "Re-run CMake if any of its inputs changed.",
|
||||||
"RERUN_CMAKE",
|
"RERUN_CMAKE",
|
||||||
/*outputs=*/cmNinjaDeps(1, NINJA_BUILD_FILE),
|
/*outputs=*/cmNinjaDeps(1, ninjaBuildFile),
|
||||||
/*explicitDeps=*/cmNinjaDeps(), implicitDeps,
|
/*explicitDeps=*/cmNinjaDeps(), implicitDeps,
|
||||||
/*orderOnlyDeps=*/cmNinjaDeps(), variables);
|
/*orderOnlyDeps=*/cmNinjaDeps(), variables);
|
||||||
|
|
||||||
|
@ -1223,7 +1245,7 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
|
||||||
/*restat=*/"",
|
/*restat=*/"",
|
||||||
/*generator=*/false);
|
/*generator=*/false);
|
||||||
WriteBuild(os, "Clean all the built files.", "CLEAN",
|
WriteBuild(os, "Clean all the built files.", "CLEAN",
|
||||||
/*outputs=*/cmNinjaDeps(1, "clean"),
|
/*outputs=*/cmNinjaDeps(1, this->NinjaOutputPath("clean")),
|
||||||
/*explicitDeps=*/cmNinjaDeps(),
|
/*explicitDeps=*/cmNinjaDeps(),
|
||||||
/*implicitDeps=*/cmNinjaDeps(),
|
/*implicitDeps=*/cmNinjaDeps(),
|
||||||
/*orderOnlyDeps=*/cmNinjaDeps(),
|
/*orderOnlyDeps=*/cmNinjaDeps(),
|
||||||
|
@ -1242,9 +1264,35 @@ void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os)
|
||||||
/*restat=*/"",
|
/*restat=*/"",
|
||||||
/*generator=*/false);
|
/*generator=*/false);
|
||||||
WriteBuild(os, "Print all primary targets available.", "HELP",
|
WriteBuild(os, "Print all primary targets available.", "HELP",
|
||||||
/*outputs=*/cmNinjaDeps(1, "help"),
|
/*outputs=*/cmNinjaDeps(1, this->NinjaOutputPath("help")),
|
||||||
/*explicitDeps=*/cmNinjaDeps(),
|
/*explicitDeps=*/cmNinjaDeps(),
|
||||||
/*implicitDeps=*/cmNinjaDeps(),
|
/*implicitDeps=*/cmNinjaDeps(),
|
||||||
/*orderOnlyDeps=*/cmNinjaDeps(),
|
/*orderOnlyDeps=*/cmNinjaDeps(),
|
||||||
/*variables=*/cmNinjaVars());
|
/*variables=*/cmNinjaVars());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmGlobalNinjaGenerator::InitOutputPathPrefix()
|
||||||
|
{
|
||||||
|
this->OutputPathPrefix =
|
||||||
|
this->LocalGenerators[0]->GetMakefile()->GetSafeDefinition(
|
||||||
|
"CMAKE_NINJA_OUTPUT_PATH_PREFIX");
|
||||||
|
EnsureTrailingSlash(this->OutputPathPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cmGlobalNinjaGenerator::NinjaOutputPath(std::string const& path)
|
||||||
|
{
|
||||||
|
if (!this->HasOutputPathPrefix() || cmSystemTools::FileIsFullPath(path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return this->OutputPathPrefix + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmGlobalNinjaGenerator::StripNinjaOutputPathPrefixAsSuffix(
|
||||||
|
std::string& path)
|
||||||
|
{
|
||||||
|
if (path.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EnsureTrailingSlash(path);
|
||||||
|
cmStripSuffixIfExists(path, this->OutputPathPrefix);
|
||||||
|
}
|
||||||
|
|
|
@ -314,6 +314,10 @@ public:
|
||||||
static std::string RequiredNinjaVersionForConsolePool() { return "1.5"; }
|
static std::string RequiredNinjaVersionForConsolePool() { return "1.5"; }
|
||||||
bool SupportsConsolePool() const;
|
bool SupportsConsolePool() const;
|
||||||
|
|
||||||
|
std::string NinjaOutputPath(std::string const& path);
|
||||||
|
bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); }
|
||||||
|
void StripNinjaOutputPathPrefixAsSuffix(std::string& path);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Generate();
|
virtual void Generate();
|
||||||
|
|
||||||
|
@ -397,6 +401,13 @@ private:
|
||||||
|
|
||||||
std::string NinjaCommand;
|
std::string NinjaCommand;
|
||||||
std::string NinjaVersion;
|
std::string NinjaVersion;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void InitOutputPathPrefix();
|
||||||
|
|
||||||
|
std::string OutputPathPrefix;
|
||||||
|
std::string TargetAll;
|
||||||
|
std::string CMakeCacheFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ! cmGlobalNinjaGenerator_h
|
#endif // ! cmGlobalNinjaGenerator_h
|
||||||
|
|
|
@ -123,7 +123,8 @@ cmGlobalNinjaGenerator* cmLocalNinjaGenerator::GetGlobalNinjaGenerator()
|
||||||
std::string cmLocalNinjaGenerator::ConvertToLinkReference(
|
std::string cmLocalNinjaGenerator::ConvertToLinkReference(
|
||||||
std::string const& lib, OutputFormat format)
|
std::string const& lib, OutputFormat format)
|
||||||
{
|
{
|
||||||
return this->Convert(lib, HOME_OUTPUT, format);
|
std::string path = this->GetGlobalNinjaGenerator()->ConvertToNinjaPath(lib);
|
||||||
|
return this->ConvertToOutputFormat(path, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cmLocalNinjaGenerator::ConvertToIncludeReference(
|
std::string cmLocalNinjaGenerator::ConvertToIncludeReference(
|
||||||
|
@ -224,8 +225,13 @@ void cmLocalNinjaGenerator::WriteNinjaFilesInclusion(std::ostream& os)
|
||||||
cmGlobalNinjaGenerator::WriteDivider(os);
|
cmGlobalNinjaGenerator::WriteDivider(os);
|
||||||
os << "# Include auxiliary files.\n"
|
os << "# Include auxiliary files.\n"
|
||||||
<< "\n";
|
<< "\n";
|
||||||
cmGlobalNinjaGenerator::WriteInclude(
|
cmGlobalNinjaGenerator* ng = this->GetGlobalNinjaGenerator();
|
||||||
os, cmGlobalNinjaGenerator::NINJA_RULES_FILE, "Include rules file.");
|
std::string const ninjaRulesFile =
|
||||||
|
ng->NinjaOutputPath(cmGlobalNinjaGenerator::NINJA_RULES_FILE);
|
||||||
|
std::string const rulesFilePath =
|
||||||
|
ng->EncodeIdent(ng->EncodePath(ninjaRulesFile), os);
|
||||||
|
cmGlobalNinjaGenerator::WriteInclude(os, rulesFilePath,
|
||||||
|
"Include rules file.");
|
||||||
os << "\n";
|
os << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -652,7 +652,8 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
|
||||||
cmNinjaDeps(1, targetOutputReal), emptyDeps, emptyDeps, symlinkVars);
|
cmNinjaDeps(1, targetOutputReal), emptyDeps, emptyDeps, symlinkVars);
|
||||||
} else {
|
} else {
|
||||||
cmNinjaDeps symlinks;
|
cmNinjaDeps symlinks;
|
||||||
const std::string soName = this->GetTargetFilePath(this->TargetNameSO);
|
std::string const soName =
|
||||||
|
this->ConvertToNinjaPath(this->GetTargetFilePath(this->TargetNameSO));
|
||||||
// If one link has to be created.
|
// If one link has to be created.
|
||||||
if (targetOutputReal == soName || targetOutput == soName) {
|
if (targetOutputReal == soName || targetOutput == soName) {
|
||||||
symlinkVars["SONAME"] = soName;
|
symlinkVars["SONAME"] = soName;
|
||||||
|
|
|
@ -521,8 +521,10 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
|
||||||
std::string const language = source->GetLanguage();
|
std::string const language = source->GetLanguage();
|
||||||
std::string const sourceFileName =
|
std::string const sourceFileName =
|
||||||
language == "RC" ? source->GetFullPath() : this->GetSourceFilePath(source);
|
language == "RC" ? source->GetFullPath() : this->GetSourceFilePath(source);
|
||||||
std::string const objectDir = this->GeneratorTarget->GetSupportDirectory();
|
std::string const objectDir =
|
||||||
std::string const objectFileName = this->GetObjectFilePath(source);
|
this->ConvertToNinjaPath(this->GeneratorTarget->GetSupportDirectory());
|
||||||
|
std::string const objectFileName =
|
||||||
|
this->ConvertToNinjaPath(this->GetObjectFilePath(source));
|
||||||
std::string const objectFileDir =
|
std::string const objectFileDir =
|
||||||
cmSystemTools::GetFilenamePath(objectFileName);
|
cmSystemTools::GetFilenamePath(objectFileName);
|
||||||
|
|
||||||
|
@ -582,9 +584,9 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
|
||||||
EnsureParentDirectoryExists(objectFileName);
|
EnsureParentDirectoryExists(objectFileName);
|
||||||
|
|
||||||
vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
|
vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
|
||||||
ConvertToNinjaPath(objectDir), cmLocalGenerator::SHELL);
|
objectDir, cmLocalGenerator::SHELL);
|
||||||
vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
|
vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
|
||||||
ConvertToNinjaPath(objectFileDir), cmLocalGenerator::SHELL);
|
objectFileDir, cmLocalGenerator::SHELL);
|
||||||
|
|
||||||
this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
|
this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
|
||||||
vars);
|
vars);
|
||||||
|
@ -667,10 +669,12 @@ void cmNinjaTargetGenerator::EnsureDirectoryExists(
|
||||||
if (cmSystemTools::FileIsFullPath(path.c_str())) {
|
if (cmSystemTools::FileIsFullPath(path.c_str())) {
|
||||||
cmSystemTools::MakeDirectory(path.c_str());
|
cmSystemTools::MakeDirectory(path.c_str());
|
||||||
} else {
|
} else {
|
||||||
const std::string fullPath = std::string(this->GetGlobalGenerator()
|
cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator();
|
||||||
->GetCMakeInstance()
|
std::string fullPath =
|
||||||
->GetHomeOutputDirectory()) +
|
std::string(gg->GetCMakeInstance()->GetHomeOutputDirectory());
|
||||||
"/" + path;
|
// Also ensures their is a trailing slash.
|
||||||
|
gg->StripNinjaOutputPathPrefixAsSuffix(fullPath);
|
||||||
|
fullPath += path;
|
||||||
cmSystemTools::MakeDirectory(fullPath.c_str());
|
cmSystemTools::MakeDirectory(fullPath.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,8 @@ void cmNinjaUtilityTargetGenerator::Generate()
|
||||||
{
|
{
|
||||||
std::string utilCommandName = cmake::GetCMakeFilesDirectoryPostSlash();
|
std::string utilCommandName = cmake::GetCMakeFilesDirectoryPostSlash();
|
||||||
utilCommandName += this->GetTargetName() + ".util";
|
utilCommandName += this->GetTargetName() + ".util";
|
||||||
|
utilCommandName =
|
||||||
|
this->GetGlobalGenerator()->NinjaOutputPath(utilCommandName);
|
||||||
|
|
||||||
std::vector<std::string> commands;
|
std::vector<std::string> commands;
|
||||||
cmNinjaDeps deps, outputs, util_outputs(1, utilCommandName);
|
cmNinjaDeps deps, outputs, util_outputs(1, utilCommandName);
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
add_custom_target(check_no_prefix_sub_dir ALL
|
||||||
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
|
"-DNINJA_OUTPUT_PATH_PREFIX=${CMAKE_NINJA_OUTPUT_PATH_PREFIX}"
|
||||||
|
"-DCUR_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
|
-P "${CMAKE_CURRENT_SOURCE_DIR}/CheckNoPrefixSubDirScript.cmake"
|
||||||
|
VERBATIM
|
||||||
|
)
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Check that the prefix sub-directory is not repeated.
|
||||||
|
|
||||||
|
if(EXISTS "${CUR_BIN_DIR}/${NINJA_OUTPUT_PATH_PREFIX}")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"no sub directory named after the CMAKE_NINJA_OUTPUT_PATH_PREFIX "
|
||||||
|
"should be in the binary directory."
|
||||||
|
)
|
||||||
|
endif()
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Add rules to check the generated executable works.
|
||||||
|
|
||||||
|
set(hello_output "${CMAKE_CURRENT_BINARY_DIR}/hello.output")
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${hello_output}"
|
||||||
|
COMMAND "$<TARGET_FILE:hello>" > "${hello_output}"
|
||||||
|
DEPENDS hello
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT DEFINED HELLO_OUTPUT_STRING)
|
||||||
|
set(HELLO_OUTPUT_STRING "Hello world!\n")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(hello_output_ref "${CMAKE_CURRENT_BINARY_DIR}/hello.output.ref")
|
||||||
|
file(WRITE "${hello_output_ref}" "${HELLO_OUTPUT_STRING}")
|
||||||
|
|
||||||
|
add_custom_target(check_output ALL
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E compare_files
|
||||||
|
"${hello_output}" "${hello_output_ref}"
|
||||||
|
DEPENDS "${hello_output}" "${hello_output_ref}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(hello NONE)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT hello.copy.c
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
|
||||||
|
hello.copy.c
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
|
)
|
||||||
|
add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/hello.copy.c")
|
||||||
|
|
||||||
|
include(CheckNoPrefixSubDir.cmake)
|
|
@ -0,0 +1,5 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(hello C)
|
||||||
|
add_executable(hello hello.c)
|
||||||
|
include(CheckOutput.cmake)
|
||||||
|
include(CheckNoPrefixSubDir.cmake)
|
|
@ -1,5 +1,20 @@
|
||||||
include(RunCMake)
|
include(RunCMake)
|
||||||
|
|
||||||
|
# Detect ninja version so we know what tests can be supported.
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${RunCMake_MAKE_PROGRAM}" --version
|
||||||
|
OUTPUT_VARIABLE ninja_out
|
||||||
|
ERROR_VARIABLE ninja_out
|
||||||
|
RESULT_VARIABLE ninja_res
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
if(ninja_res EQUAL 0 AND "x${ninja_out}" MATCHES "^x[0-9]+\\.[0-9]+")
|
||||||
|
set(ninja_version "${ninja_out}")
|
||||||
|
message(STATUS "ninja version: ${ninja_version}")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "'ninja --version' reported:\n${ninja_out}")
|
||||||
|
endif()
|
||||||
|
|
||||||
function(run_CMP0058 case)
|
function(run_CMP0058 case)
|
||||||
# Use a single build tree for a few tests without cleaning.
|
# Use a single build tree for a few tests without cleaning.
|
||||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0058-${case}-build)
|
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0058-${case}-build)
|
||||||
|
@ -32,3 +47,153 @@ function(run_SubDir)
|
||||||
run_cmake_command(SubDir-build ${CMAKE_COMMAND} --build . --target ${SubDir_all})
|
run_cmake_command(SubDir-build ${CMAKE_COMMAND} --build . --target ${SubDir_all})
|
||||||
endfunction()
|
endfunction()
|
||||||
run_SubDir()
|
run_SubDir()
|
||||||
|
|
||||||
|
function(run_ninja dir)
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${RunCMake_MAKE_PROGRAM}"
|
||||||
|
WORKING_DIRECTORY "${dir}"
|
||||||
|
OUTPUT_VARIABLE ninja_stdout
|
||||||
|
ERROR_VARIABLE ninja_stderr
|
||||||
|
RESULT_VARIABLE ninja_result
|
||||||
|
)
|
||||||
|
if(NOT ninja_result EQUAL 0)
|
||||||
|
message(STATUS "
|
||||||
|
============ beginning of ninja's stdout ============
|
||||||
|
${ninja_stdout}
|
||||||
|
=============== end of ninja's stdout ===============
|
||||||
|
")
|
||||||
|
message(STATUS "
|
||||||
|
============ beginning of ninja's stderr ============
|
||||||
|
${ninja_stderr}
|
||||||
|
=============== end of ninja's stderr ===============
|
||||||
|
")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"top ninja build failed exited with status ${ninja_result}")
|
||||||
|
endif()
|
||||||
|
endfunction(run_ninja)
|
||||||
|
|
||||||
|
function(sleep delay)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E sleep ${delay}
|
||||||
|
RESULT_VARIABLE result
|
||||||
|
)
|
||||||
|
if(NOT result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "failed to sleep for ${delay} second.")
|
||||||
|
endif()
|
||||||
|
endfunction(sleep)
|
||||||
|
|
||||||
|
function(touch path)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E touch ${path}
|
||||||
|
RESULT_VARIABLE result
|
||||||
|
)
|
||||||
|
if(NOT result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "failed to touch main ${path} file.")
|
||||||
|
endif()
|
||||||
|
endfunction(touch)
|
||||||
|
|
||||||
|
macro(ninja_escape_path path out)
|
||||||
|
string(REPLACE "\$ " "\$\$" "${out}" "${path}")
|
||||||
|
string(REPLACE " " "\$ " "${out}" "${${out}}")
|
||||||
|
string(REPLACE ":" "\$:" "${out}" "${${out}}")
|
||||||
|
endmacro(ninja_escape_path)
|
||||||
|
|
||||||
|
macro(shell_escape string out)
|
||||||
|
string(REPLACE "\"" "\\\"" "${out}" "${string}")
|
||||||
|
endmacro(shell_escape)
|
||||||
|
|
||||||
|
function(run_sub_cmake test ninja_output_path_prefix)
|
||||||
|
set(top_build_dir "${RunCMake_BINARY_DIR}/${test}-build/")
|
||||||
|
file(REMOVE_RECURSE "${top_build_dir}")
|
||||||
|
file(MAKE_DIRECTORY "${top_build_dir}")
|
||||||
|
|
||||||
|
ninja_escape_path("${ninja_output_path_prefix}"
|
||||||
|
escaped_ninja_output_path_prefix)
|
||||||
|
|
||||||
|
# Generate top build ninja file.
|
||||||
|
set(top_build_ninja "${top_build_dir}/build.ninja")
|
||||||
|
shell_escape("${top_build_ninja}" escaped_top_build_ninja)
|
||||||
|
set(build_ninja_dep "${top_build_dir}/build_ninja_dep")
|
||||||
|
ninja_escape_path("${build_ninja_dep}" escaped_build_ninja_dep)
|
||||||
|
shell_escape("${CMAKE_COMMAND}" escaped_CMAKE_COMMAND)
|
||||||
|
file(WRITE "${build_ninja_dep}" "fake dependency of top build.ninja file\n")
|
||||||
|
if(WIN32)
|
||||||
|
set(cmd_prefix "cmd.exe /C \"")
|
||||||
|
set(cmd_suffix "\"")
|
||||||
|
else()
|
||||||
|
set(cmd_prefix "")
|
||||||
|
set(cmd_suffix "")
|
||||||
|
endif()
|
||||||
|
file(WRITE "${top_build_ninja}" "\
|
||||||
|
subninja ${escaped_ninja_output_path_prefix}/build.ninja
|
||||||
|
default ${escaped_ninja_output_path_prefix}/all
|
||||||
|
|
||||||
|
# Sleep for 1 second before to regenerate to make sure the timestamp of
|
||||||
|
# the top build.ninja will be strictly greater than the timestamp of the
|
||||||
|
# sub/build.ninja file. We assume the system as 1 sec timestamp resolution.
|
||||||
|
rule RERUN
|
||||||
|
command = ${cmd_prefix}\"${escaped_CMAKE_COMMAND}\" -E sleep 1 && \"${escaped_CMAKE_COMMAND}\" -E touch \"${escaped_top_build_ninja}\"${cmd_suffix}
|
||||||
|
description = Testing regeneration
|
||||||
|
generator = 1
|
||||||
|
|
||||||
|
build build.ninja: RERUN ${escaped_build_ninja_dep} || ${escaped_ninja_output_path_prefix}/build.ninja
|
||||||
|
pool = console
|
||||||
|
")
|
||||||
|
|
||||||
|
# Run sub cmake project.
|
||||||
|
set(RunCMake_TEST_OPTIONS "-DCMAKE_NINJA_OUTPUT_PATH_PREFIX=${ninja_output_path_prefix}")
|
||||||
|
set(RunCMake_TEST_BINARY_DIR "${top_build_dir}/${ninja_output_path_prefix}")
|
||||||
|
run_cmake(${test})
|
||||||
|
|
||||||
|
# Check there is no 'default' statement in Ninja file generated by CMake.
|
||||||
|
set(sub_build_ninja "${RunCMake_TEST_BINARY_DIR}/build.ninja")
|
||||||
|
file(READ "${sub_build_ninja}" sub_build_ninja_file)
|
||||||
|
if(sub_build_ninja_file MATCHES "\ndefault [^\n][^\n]*all\n")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"unexpected 'default' statement found in '${sub_build_ninja}'")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Run ninja from the top build directory.
|
||||||
|
run_ninja("${top_build_dir}")
|
||||||
|
|
||||||
|
# Test regeneration rules run in order.
|
||||||
|
set(main_cmakelists "${RunCMake_SOURCE_DIR}/CMakeLists.txt")
|
||||||
|
sleep(1) # Assume the system as 1 sec timestamp resolution.
|
||||||
|
touch("${main_cmakelists}")
|
||||||
|
touch("${build_ninja_dep}")
|
||||||
|
run_ninja("${top_build_dir}")
|
||||||
|
file(TIMESTAMP "${main_cmakelists}" mtime_main_cmakelists UTC)
|
||||||
|
file(TIMESTAMP "${sub_build_ninja}" mtime_sub_build_ninja UTC)
|
||||||
|
file(TIMESTAMP "${top_build_ninja}" mtime_top_build_ninja UTC)
|
||||||
|
|
||||||
|
# Check sub build.ninja is regenerated.
|
||||||
|
if(mtime_main_cmakelists STRGREATER mtime_sub_build_ninja)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"sub build.ninja not regenerated:
|
||||||
|
CMakeLists.txt = ${mtime_main_cmakelists}
|
||||||
|
sub/build.ninja = ${mtime_sub_build_ninja}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check top build.ninja is regenerated after sub build.ninja.
|
||||||
|
if(NOT mtime_top_build_ninja STRGREATER mtime_sub_build_ninja)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"top build.ninja not regenerated strictly after sub build.ninja:
|
||||||
|
sub/build.ninja = ${mtime_sub_build_ninja}
|
||||||
|
build.ninja = ${mtime_top_build_ninja}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endfunction(run_sub_cmake)
|
||||||
|
|
||||||
|
if("${ninja_version}" VERSION_LESS 1.6)
|
||||||
|
message(WARNING "Ninja is too old; skipping rest of test.")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach(ninja_output_path_prefix "sub space" "sub")
|
||||||
|
run_sub_cmake(Executable "${ninja_output_path_prefix}")
|
||||||
|
run_sub_cmake(StaticLib "${ninja_output_path_prefix}")
|
||||||
|
run_sub_cmake(SharedLib "${ninja_output_path_prefix}")
|
||||||
|
run_sub_cmake(TwoLibs "${ninja_output_path_prefix}")
|
||||||
|
run_sub_cmake(SubDirPrefix "${ninja_output_path_prefix}")
|
||||||
|
run_sub_cmake(CustomCommandWorkingDirectory "${ninja_output_path_prefix}")
|
||||||
|
endforeach(ninja_output_path_prefix)
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(hello C)
|
||||||
|
add_library(greeting SHARED greeting.c)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
add_executable(hello hello_with_greeting.c)
|
||||||
|
target_link_libraries(hello greeting)
|
||||||
|
include(CheckOutput.cmake)
|
||||||
|
include(CheckNoPrefixSubDir.cmake)
|
|
@ -0,0 +1,9 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(hello C)
|
||||||
|
add_definitions(-DGREETING_STATIC)
|
||||||
|
add_library(greeting STATIC greeting.c)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
add_executable(hello hello_with_greeting.c)
|
||||||
|
target_link_libraries(hello greeting)
|
||||||
|
include(CheckOutput.cmake)
|
||||||
|
include(CheckNoPrefixSubDir.cmake)
|
|
@ -0,0 +1,8 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(hello C)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
|
||||||
|
add_subdirectory(SubDirPrefix)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
add_executable(hello hello_sub_greeting.c)
|
||||||
|
target_link_libraries(hello greeting)
|
||||||
|
include(CheckOutput.cmake)
|
|
@ -0,0 +1,2 @@
|
||||||
|
add_library(greeting SHARED greeting.c)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(GREETING_STATIC)
|
||||||
|
__declspec(dllexport)
|
||||||
|
#endif
|
||||||
|
void greeting(void)
|
||||||
|
{
|
||||||
|
printf("Hello world!\n");
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#if defined(_WIN32) && !defined(GREETING_STATIC)
|
||||||
|
__declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
void greeting(void);
|
|
@ -0,0 +1,17 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
project(hello C)
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib-static")
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||||
|
|
||||||
|
add_library(greeting SHARED greeting.c)
|
||||||
|
add_library(greeting2 STATIC greeting2.c)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
add_executable(hello hello_with_two_greetings.c)
|
||||||
|
target_link_libraries(hello greeting greeting2)
|
||||||
|
|
||||||
|
set(HELLO_OUTPUT_STRING "Hello world!\nHello world 2!\n")
|
||||||
|
include(CheckOutput.cmake)
|
||||||
|
|
||||||
|
include(CheckNoPrefixSubDir.cmake)
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(_WIN32) && !defined(GREETING_STATIC)
|
||||||
|
__declspec(dllexport)
|
||||||
|
#endif
|
||||||
|
void greeting(void)
|
||||||
|
{
|
||||||
|
printf("Hello world!\n");
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#if defined(_WIN32) && !defined(GREETING_STATIC)
|
||||||
|
__declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
void greeting(void);
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void greeting2(void)
|
||||||
|
{
|
||||||
|
printf("Hello world 2!\n");
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
void greeting2(void);
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("Hello world!\n");
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include <SubDirPrefix/greeting.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
greeting();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include <greeting.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
greeting();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <greeting.h>
|
||||||
|
#include <greeting2.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
greeting();
|
||||||
|
greeting2();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue