link_subcommand.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/driver/link_subcommand.h"
  5. #include "llvm/TargetParser/Triple.h"
  6. #include "toolchain/driver/clang_runner.h"
  7. namespace Carbon {
  8. auto LinkOptions::Build(CommandLine::CommandBuilder& b) -> void {
  9. b.AddStringPositionalArg(
  10. {
  11. .name = "OBJECT_FILE",
  12. .help = R"""(
  13. The input object files.
  14. If empty, there must be extra Clang link arguments that provide object files
  15. intermingled with linking flags.
  16. )""",
  17. },
  18. [&](auto& arg_b) { arg_b.Append(&object_filenames); });
  19. b.AddStringOption(
  20. {
  21. .name = "output",
  22. .value_name = "FILE",
  23. .help = R"""(
  24. The linked file name. The output is always a linked binary.
  25. If not provided, there must be extra Clang link arguments that include
  26. specifying the output of the link. This allows supporting build systems that
  27. intermingle the output flag with arbitrary other linker flags that need to use
  28. legacy parsing logic.
  29. )""",
  30. },
  31. [&](auto& arg_b) { arg_b.Set(&output_filename); });
  32. b.AddStringPositionalArg(
  33. {
  34. .name = "EXTRA_CLANG_LINK_ARGS",
  35. .help = R"""(
  36. Extra arguments to pass to Clang when forming the link command. This is
  37. primarily useful for expanding `LDFLAGS` or other baseline linking flags in a
  38. build system.
  39. These can also be used to pass object files to the link in the event your build
  40. system mixes object files and linker flags.
  41. )""",
  42. },
  43. [&](auto& arg_b) { arg_b.Append(&extra_clang_args); });
  44. codegen_options.Build(b);
  45. }
  46. static constexpr CommandLine::CommandInfo SubcommandInfo = {
  47. .name = "link",
  48. .help = R"""(
  49. Link Carbon executables.
  50. This subcommand links Carbon executables by combining object files.
  51. TODO: Support linking binary libraries, both archives and shared libraries.
  52. TODO: Support linking against binary libraries.
  53. )""",
  54. };
  55. LinkSubcommand::LinkSubcommand() : DriverSubcommand(SubcommandInfo) {}
  56. auto LinkSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
  57. // TODO: Currently we use the Clang driver to link. This works well on Unix
  58. // OSes but we likely need to directly build logic to invoke `link.exe` on
  59. // Windows where `cl.exe` doesn't typically cover that logic.
  60. // Use a reasonably large small vector here to minimize allocations. We expect
  61. // to link reasonably large numbers of object files.
  62. llvm::SmallVector<llvm::StringRef, 128> clang_args;
  63. // We link using a C++ mode of the driver.
  64. clang_args.push_back("--driver-mode=g++");
  65. // Pass the target down to Clang to pick up the correct defaults.
  66. std::string target_arg =
  67. llvm::formatv("--target={0}", options_.codegen_options.target).str();
  68. clang_args.push_back(target_arg);
  69. if (!options_.output_filename.empty()) {
  70. clang_args.push_back("-o");
  71. clang_args.push_back(options_.output_filename);
  72. } else if (options_.extra_clang_args.empty()) {
  73. CARBON_DIAGNOSTIC(LinkOutputOptionMissing, Error,
  74. "no output specified to a link command and no extra "
  75. "Clang options that can provide an output");
  76. driver_env.emitter.Emit(LinkOutputOptionMissing);
  77. return {.success = false};
  78. }
  79. if (options_.object_filenames.empty() && options_.extra_clang_args.empty()) {
  80. CARBON_DIAGNOSTIC(LinkObjectFilesMissing, Error,
  81. "no object files provided to link command and no extra "
  82. "Clang options that could provide them");
  83. driver_env.emitter.Emit(LinkObjectFilesMissing);
  84. return {.success = false};
  85. }
  86. // Note that we append any extra Clang args before our object filenames. This
  87. // allows us to propagate object filenames that collide with Clang flags using
  88. // `--` before the filenames. While in theory, this could create a problem in
  89. // the presence of mixtures of object files in the two lists and the order
  90. // being dependent, we don't expect that in practice.
  91. clang_args.append(options_.extra_clang_args.begin(),
  92. options_.extra_clang_args.end());
  93. clang_args.push_back("--");
  94. clang_args.append(options_.object_filenames.begin(),
  95. options_.object_filenames.end());
  96. ClangRunner runner(driver_env.installation, driver_env.fs,
  97. driver_env.vlog_stream);
  98. ErrorOr<bool> run_result =
  99. driver_env.prebuilt_runtimes
  100. ? runner.RunWithPrebuiltRuntimes(clang_args,
  101. *driver_env.prebuilt_runtimes,
  102. driver_env.enable_leaking)
  103. : driver_env.build_runtimes_on_demand
  104. ? runner.Run(clang_args, driver_env.runtimes_cache,
  105. *driver_env.thread_pool, driver_env.enable_leaking)
  106. : runner.RunWithNoRuntimes(clang_args, driver_env.enable_leaking);
  107. if (!run_result.ok()) {
  108. // This is not a Clang failure, but a failure to even run Clang, so we need
  109. // to diagnose it here.
  110. CARBON_DIAGNOSTIC(FailureRunningClangToLink, Error,
  111. "failure running `clang` to perform linking: {0}",
  112. std::string);
  113. driver_env.emitter.Emit(FailureRunningClangToLink,
  114. run_result.error().message());
  115. return {.success = false};
  116. }
  117. // Successfully ran Clang to perform the link, return its result.
  118. return {.success = *run_result};
  119. }
  120. } // namespace Carbon