mirror of
https://github.com/Kitware/CMake.git
synced 2026-04-23 22:58:37 -05:00
Add options to run ldd -u -r as a "link-what-you-use" tool
Create a LINK_WHAT_YOU_USE target property and corresponding CMAKE_LINK_WHAT_YOU_USE variable to enable this behavior. Extend link commands by running `ldd -u -r` to detect shared libraries that are linked but not needed.
This commit is contained in:
@@ -187,6 +187,9 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
|
||||
this->LocalGenerator->AppendFlags(
|
||||
linkFlags, this->Makefile->GetDefinition(export_flag_var));
|
||||
}
|
||||
if (this->GeneratorTarget->GetProperty("LINK_WHAT_YOU_USE")) {
|
||||
this->LocalGenerator->AppendFlags(linkFlags, " -Wl,--no-as-needed");
|
||||
}
|
||||
|
||||
// Add language feature flags.
|
||||
this->AddFeatureFlags(flags, linkLanguage);
|
||||
@@ -356,6 +359,15 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
|
||||
vars.LinkFlags = linkFlags.c_str();
|
||||
vars.Manifests = manifests.c_str();
|
||||
|
||||
if (this->GeneratorTarget->GetProperty("LINK_WHAT_YOU_USE")) {
|
||||
std::string cmakeCommand =
|
||||
this->Convert(cmSystemTools::GetCMakeCommand(), cmLocalGenerator::NONE,
|
||||
cmLocalGenerator::SHELL);
|
||||
cmakeCommand += " -E __run_iwyu --lwyu=";
|
||||
cmakeCommand += targetOutPathReal;
|
||||
real_link_commands.push_back(cmakeCommand);
|
||||
}
|
||||
|
||||
// Expand placeholders in the commands.
|
||||
this->LocalGenerator->TargetImplib = targetOutPathImport;
|
||||
for (std::vector<std::string>::iterator i = real_link_commands.begin();
|
||||
|
||||
@@ -163,6 +163,9 @@ void cmMakefileLibraryTargetGenerator::WriteSharedLibraryRules(bool relink)
|
||||
extraFlags, "CMAKE_SHARED_LINKER_FLAGS", this->ConfigName);
|
||||
this->AddModuleDefinitionFlag(extraFlags);
|
||||
|
||||
if (this->GeneratorTarget->GetProperty("LINK_WHAT_YOU_USE")) {
|
||||
this->LocalGenerator->AppendFlags(extraFlags, " -Wl,--no-as-needed");
|
||||
}
|
||||
this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
|
||||
}
|
||||
|
||||
@@ -682,6 +685,15 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
|
||||
// Get the set of commands.
|
||||
std::string linkRule = this->GetLinkRule(linkRuleVar);
|
||||
cmSystemTools::ExpandListArgument(linkRule, real_link_commands);
|
||||
if (this->GeneratorTarget->GetProperty("LINK_WHAT_YOU_USE") &&
|
||||
(this->GeneratorTarget->GetType() == cmState::SHARED_LIBRARY)) {
|
||||
std::string cmakeCommand =
|
||||
this->Convert(cmSystemTools::GetCMakeCommand(),
|
||||
cmLocalGenerator::NONE, cmLocalGenerator::SHELL);
|
||||
cmakeCommand += " -E __run_iwyu --lwyu=";
|
||||
cmakeCommand += targetOutPathReal;
|
||||
real_link_commands.push_back(cmakeCommand);
|
||||
}
|
||||
|
||||
// Expand placeholders.
|
||||
for (std::vector<std::string>::iterator i = real_link_commands.begin();
|
||||
@@ -728,6 +740,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
|
||||
commands.insert(commands.end(), commands1.begin(), commands1.end());
|
||||
commands1.clear();
|
||||
}
|
||||
|
||||
// Add the post-build rules when building but not when relinking.
|
||||
if (!relink) {
|
||||
this->LocalGenerator->AppendCustomCommands(
|
||||
|
||||
@@ -295,6 +295,22 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd()
|
||||
const char* linkCmd = mf->GetDefinition(linkCmdVar);
|
||||
if (linkCmd) {
|
||||
cmSystemTools::ExpandListArgument(linkCmd, linkCmds);
|
||||
if (this->GetGeneratorTarget()->GetProperty("LINK_WHAT_YOU_USE")) {
|
||||
std::string cmakeCommand =
|
||||
this->GetLocalGenerator()->ConvertToOutputFormat(
|
||||
cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
|
||||
cmakeCommand += " -E __run_iwyu --lwyu=";
|
||||
cmGeneratorTarget& gt = *this->GetGeneratorTarget();
|
||||
const std::string cfgName = this->GetConfigName();
|
||||
std::string targetOutput = ConvertToNinjaPath(gt.GetFullPath(cfgName));
|
||||
std::string targetOutputReal =
|
||||
this->ConvertToNinjaPath(gt.GetFullPath(cfgName,
|
||||
/*implib=*/false,
|
||||
/*realpath=*/true));
|
||||
cmakeCommand += targetOutputReal;
|
||||
cmakeCommand += " || true";
|
||||
linkCmds.push_back(cmakeCommand);
|
||||
}
|
||||
return linkCmds;
|
||||
}
|
||||
}
|
||||
@@ -467,6 +483,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
|
||||
vars["MANIFESTS"] = this->GetManifests();
|
||||
|
||||
vars["LINK_PATH"] = frameworkPath + linkPath;
|
||||
std::string lwyuFlags;
|
||||
if (genTarget.GetProperty("LINK_WHAT_YOU_USE")) {
|
||||
lwyuFlags = " -Wl,--no-as-needed";
|
||||
}
|
||||
|
||||
// Compute architecture specific link flags. Yes, these go into a different
|
||||
// variable for executables, probably due to a mistake made when duplicating
|
||||
@@ -474,16 +494,17 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
|
||||
if (targetType == cmState::EXECUTABLE) {
|
||||
std::string t = vars["FLAGS"];
|
||||
localGen.AddArchitectureFlags(t, &genTarget, TargetLinkLanguage, cfgName);
|
||||
t += lwyuFlags;
|
||||
vars["FLAGS"] = t;
|
||||
} else {
|
||||
std::string t = vars["ARCH_FLAGS"];
|
||||
localGen.AddArchitectureFlags(t, &genTarget, TargetLinkLanguage, cfgName);
|
||||
vars["ARCH_FLAGS"] = t;
|
||||
t = "";
|
||||
t += lwyuFlags;
|
||||
localGen.AddLanguageFlags(t, TargetLinkLanguage, cfgName);
|
||||
vars["LANGUAGE_COMPILE_FLAGS"] = t;
|
||||
}
|
||||
|
||||
if (this->GetGeneratorTarget()->HasSOName(cfgName)) {
|
||||
vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage);
|
||||
vars["SONAME"] = this->TargetNameSO;
|
||||
@@ -607,7 +628,6 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
|
||||
vars["POST_BUILD"] = ":";
|
||||
symlinkVars["POST_BUILD"] = postBuildCmdLine;
|
||||
}
|
||||
|
||||
cmGlobalNinjaGenerator& globalGen = *this->GetGlobalGenerator();
|
||||
|
||||
int commandLineLengthLimit = -1;
|
||||
|
||||
@@ -139,6 +139,7 @@ void cmTarget::SetMakefile(cmMakefile* mf)
|
||||
this->SetPropertyDefault("C_CLANG_TIDY", 0);
|
||||
this->SetPropertyDefault("C_COMPILER_LAUNCHER", 0);
|
||||
this->SetPropertyDefault("C_INCLUDE_WHAT_YOU_USE", 0);
|
||||
this->SetPropertyDefault("LINK_WHAT_YOU_USE", 0);
|
||||
this->SetPropertyDefault("C_STANDARD", 0);
|
||||
this->SetPropertyDefault("C_STANDARD_REQUIRED", 0);
|
||||
this->SetPropertyDefault("C_EXTENSIONS", 0);
|
||||
|
||||
+31
-4
@@ -271,6 +271,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
|
||||
std::string iwyu;
|
||||
std::string tidy;
|
||||
std::string sourceFile;
|
||||
std::string lwyu;
|
||||
for (std::string::size_type cc = 2; cc < args.size(); cc++) {
|
||||
std::string const& arg = args[cc];
|
||||
if (arg == "--") {
|
||||
@@ -281,6 +282,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
|
||||
tidy = arg.substr(7);
|
||||
} else if (doing_options && cmHasLiteralPrefix(arg, "--source=")) {
|
||||
sourceFile = arg.substr(9);
|
||||
} else if (doing_options && cmHasLiteralPrefix(arg, "--lwyu=")) {
|
||||
lwyu = arg.substr(7);
|
||||
} else if (doing_options) {
|
||||
std::cerr << "__run_iwyu given unknown argument: " << arg << "\n";
|
||||
return 1;
|
||||
@@ -288,7 +291,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
|
||||
orig_cmd.push_back(arg);
|
||||
}
|
||||
}
|
||||
if (tidy.empty() && iwyu.empty()) {
|
||||
if (tidy.empty() && iwyu.empty() && lwyu.empty()) {
|
||||
std::cerr << "__run_iwyu missing --tidy= or --iwyu=\n";
|
||||
return 1;
|
||||
}
|
||||
@@ -296,7 +299,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
|
||||
std::cerr << "__run_iwyu --tidy= requires --source=\n";
|
||||
return 1;
|
||||
}
|
||||
if (orig_cmd.empty()) {
|
||||
if (orig_cmd.empty() && lwyu.empty()) {
|
||||
std::cerr << "__run_iwyu missing compile command after --\n";
|
||||
return 1;
|
||||
}
|
||||
@@ -345,13 +348,37 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
|
||||
std::cerr << "Error running '" << tidy_cmd[0] << "'\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Output the stdout from clang-tidy to stderr
|
||||
std::cerr << stdOut;
|
||||
}
|
||||
if (!lwyu.empty()) {
|
||||
// Construct the ldd -r -u (link what you use lwyu) command line
|
||||
// ldd -u -r lwuy target
|
||||
std::vector<std::string> lwyu_cmd;
|
||||
lwyu_cmd.push_back("ldd");
|
||||
lwyu_cmd.push_back("-u");
|
||||
lwyu_cmd.push_back("-r");
|
||||
lwyu_cmd.push_back(lwyu);
|
||||
|
||||
// Run the ldd -u -r command line.
|
||||
// Capture its stdout and hide its stderr.
|
||||
std::string stdOut;
|
||||
if (!cmSystemTools::RunSingleCommand(lwyu_cmd, &stdOut, 0, &ret, 0,
|
||||
cmSystemTools::OUTPUT_NONE)) {
|
||||
std::cerr << "Error running '" << lwyu_cmd[0] << "'\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Output the stdout from ldd -r -u to stderr
|
||||
// Warn if lwyu reported anything.
|
||||
if (stdOut.find("Unused direct dependencies:") != stdOut.npos) {
|
||||
std::cerr << "Warning: " << stdOut;
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
// Now run the real compiler command and return its result value.
|
||||
if (!cmSystemTools::RunSingleCommand(
|
||||
if (lwyu.empty() &&
|
||||
!cmSystemTools::RunSingleCommand(
|
||||
orig_cmd, 0, 0, &ret, 0, cmSystemTools::OUTPUT_PASSTHROUGH)) {
|
||||
std::cerr << "Error running '" << orig_cmd[0] << "'\n";
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user