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:
Bill Hoffman
2016-06-02 18:08:30 -04:00
committed by Brad King
parent ba92e11f8b
commit 96242f8022
22 changed files with 164 additions and 6 deletions
@@ -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(
+22 -2
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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;