kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/extractor/objc_extractor_bazel_main.cc (about)

     1  /*
     2   * Copyright 2016 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  // objc_extractor_bazel is a Objective-C extractor meant to be run as a Bazel
    18  // extra_action. It may be used with third_party/bazel/get_devdir.sh and
    19  // third_party/bazel/get_sdkroot.sh to fix placeholders left in arguments by
    20  // Bazel.
    21  //
    22  // For example:
    23  //
    24  //  action_listener(
    25  //    name = "extract_kzip",
    26  //    extra_actions = [":extra_action"],
    27  //    mnemonics = ["ObjcCompile"],
    28  //    visibility = ["//visibility:public"],
    29  //  )
    30  //
    31  //  extra_action(
    32  //    name = "extra_action",
    33  //    cmd = "$(location :objc_extractor_binary) \
    34  //             $(EXTRA_ACTION_FILE) \
    35  //             $(output $(ACTION_ID).objc.kzip) \
    36  //             $(location :vnames_config) \
    37  //             $(location :get_devdir) \
    38  //             $(location :get_sdkroot)",
    39  //    data = [
    40  //      ":get_devdir",
    41  //      ":get_sdkroot",
    42  //      ":vnames_config",
    43  //    ],
    44  //    out_templates = ["$(ACTION_ID).objc.kzip"],
    45  //    tools = [":objc_extractor_binary"],
    46  //  )
    47  //
    48  //  # In this example, the extractor binary is pre-built.
    49  //  filegroup(
    50  //    name = "objc_extractor_binary",
    51  //    srcs = ["objc_extractor_bazel"],
    52  //  )
    53  //
    54  //  filegroup(
    55  //    name = "vnames_config",
    56  //    srcs = ["vnames.json"],
    57  //  )
    58  //
    59  //  sh_binary(
    60  //    name = "get_devdir",
    61  //    srcs = ["get_devdir.sh"],
    62  //  )
    63  //
    64  //  sh_binary(
    65  //    name = "get_sdkroot",
    66  //    srcs = ["get_sdkroot.sh"],
    67  //  )
    68  
    69  #include <fcntl.h>
    70  #include <sys/stat.h>
    71  #include <unistd.h>
    72  
    73  #include <climits>
    74  #include <cstdio>
    75  #include <string>
    76  #include <vector>
    77  
    78  #include "absl/flags/flag.h"
    79  #include "absl/flags/parse.h"
    80  #include "absl/log/check.h"
    81  #include "absl/log/log.h"
    82  #include "absl/strings/str_format.h"
    83  #include "cxx_extractor.h"
    84  #include "google/protobuf/io/coded_stream.h"
    85  #include "google/protobuf/io/zero_copy_stream_impl.h"
    86  #include "google/protobuf/stubs/common.h"
    87  #include "kythe/cxx/common/init.h"
    88  #include "kythe/cxx/common/path_utils.h"
    89  #include "kythe/cxx/extractor/language.h"
    90  #include "objc_bazel_support.h"
    91  #include "third_party/bazel/src/main/protobuf/extra_actions_base.pb.h"
    92  
    93  ABSL_FLAG(kythe::PathCanonicalizer::Policy, canonicalize_vname_paths,
    94            kythe::PathCanonicalizer::Policy::kCleanOnly,
    95            "Policy to use when canonicalization VName paths: "
    96            "clean-only (default), prefer-relative, prefer-real.");
    97  
    98  struct XAState {
    99    std::string extra_action_file;
   100    std::string output_file;
   101    std::string vname_config;
   102    std::string devdir_script;
   103    std::string sdkroot_script;
   104  };
   105  
   106  static bool ContainsUnsupportedArg(const std::vector<std::string>& args) {
   107    for (const auto& arg : args) {
   108      // We do not support compilations using modules yet.
   109      if (arg == "-fmodules") {
   110        return true;
   111      }
   112    }
   113    return false;
   114  }
   115  
   116  static bool LoadSpawnInfo(const XAState& xa_state,
   117                            const blaze::ExtraActionInfo& info,
   118                            kythe::ExtractorConfiguration& config) {
   119    blaze::SpawnInfo spawn_info = info.GetExtension(blaze::SpawnInfo::spawn_info);
   120  
   121    std::vector<std::string> args;
   122    // If the user didn't specify a script path, don't mutate the arguments in the
   123    // extra action.
   124    if (xa_state.devdir_script.empty() || xa_state.sdkroot_script.empty()) {
   125      for (const auto& i : spawn_info.argument()) {
   126        std::string arg = i;
   127        args.push_back(arg);
   128      }
   129    } else {
   130      auto cmdPrefix = kythe::BuildEnvVarCommandPrefix(spawn_info.variable());
   131      auto devdir = kythe::RunScript(cmdPrefix + xa_state.devdir_script);
   132      auto sdkroot = kythe::RunScript(cmdPrefix + xa_state.sdkroot_script);
   133  
   134      kythe::FillWithFixedArgs(args, spawn_info, devdir, sdkroot);
   135    }
   136  
   137    if (ContainsUnsupportedArg(args)) {
   138      LOG(ERROR) << "Not extracting " << info.owner()
   139                 << " because it had an unsupported argument.";
   140      return false;
   141    }
   142  
   143    config.SetOutputFile(xa_state.output_file);
   144    config.SetArgs(args);
   145    config.SetVNameConfig(xa_state.vname_config);
   146    config.SetTargetName(info.owner());
   147    if (spawn_info.output_file_size() > 0) {
   148      config.SetCompilationOutputPath(spawn_info.output_file(0));
   149    }
   150    return true;
   151  }
   152  
   153  static bool LoadCppInfo(const XAState& xa_state,
   154                          const blaze::ExtraActionInfo& info,
   155                          kythe::ExtractorConfiguration& config) {
   156    blaze::CppCompileInfo cpp_info =
   157        info.GetExtension(blaze::CppCompileInfo::cpp_compile_info);
   158  
   159    std::vector<std::string> args;
   160    // If the user didn't specify a script path, don't mutate the arguments in the
   161    // extra action.
   162    if (xa_state.devdir_script.empty() || xa_state.sdkroot_script.empty()) {
   163      args.push_back(cpp_info.tool());
   164      for (const auto& i : cpp_info.compiler_option()) {
   165        std::string arg = i;
   166        args.push_back(arg);
   167      }
   168    } else {
   169      auto cmdPrefix = kythe::BuildEnvVarCommandPrefix(cpp_info.variable());
   170      auto devdir = kythe::RunScript(cmdPrefix + xa_state.devdir_script);
   171      auto sdkroot = kythe::RunScript(cmdPrefix + xa_state.sdkroot_script);
   172  
   173      kythe::FillWithFixedArgs(args, cpp_info, devdir, sdkroot);
   174    }
   175  
   176    if (ContainsUnsupportedArg(args)) {
   177      LOG(ERROR) << "Not extracting " << info.owner()
   178                 << " because it had an unsupported argument.";
   179      return false;
   180    }
   181  
   182    config.SetOutputFile(xa_state.output_file);
   183    config.SetArgs(args);
   184    config.SetVNameConfig(xa_state.vname_config);
   185    config.SetTargetName(info.owner());
   186    config.SetCompilationOutputPath(cpp_info.output_file());
   187    return true;
   188  }
   189  
   190  static bool LoadExtraAction(const XAState& xa_state,
   191                              kythe::ExtractorConfiguration& config) {
   192    using namespace google::protobuf::io;
   193    blaze::ExtraActionInfo info;
   194    int fd =
   195        open(xa_state.extra_action_file.c_str(), O_RDONLY, S_IREAD | S_IWRITE);
   196    CHECK_GE(fd, 0) << "Couldn't open input file " << xa_state.extra_action_file;
   197    FileInputStream file_input_stream(fd);
   198    CodedInputStream coded_input_stream(&file_input_stream);
   199    coded_input_stream.SetTotalBytesLimit(INT_MAX);
   200    CHECK(info.ParseFromCodedStream(&coded_input_stream));
   201    close(fd);
   202  
   203    if (info.HasExtension(blaze::SpawnInfo::spawn_info)) {
   204      return LoadSpawnInfo(xa_state, info, config);
   205    } else if (info.HasExtension(blaze::CppCompileInfo::cpp_compile_info)) {
   206      return LoadCppInfo(xa_state, info, config);
   207    }
   208    LOG(ERROR)
   209        << "ObjcCompile Extra Action didn't have SpawnInfo or CppCompileInfo.";
   210    return false;
   211  }
   212  
   213  int main(int argc, char* argv[]) {
   214    GOOGLE_PROTOBUF_VERIFY_VERSION;
   215    kythe::InitializeProgram(argv[0]);
   216    std::vector<char*> remain = absl::ParseCommandLine(argc, argv);
   217    if (remain.size() != 4 && remain.size() != 6) {
   218      absl::FPrintF(
   219          stderr,
   220          "Invalid number of arguments:\n\tCall as %s extra-action-file "
   221          "output-file vname-config [devdir-script sdkroot-script]\n",
   222          remain[0]);
   223      return 1;
   224    }
   225    XAState xa_state;
   226    xa_state.extra_action_file = remain[1];
   227    xa_state.output_file = remain[2];
   228    xa_state.vname_config = remain[3];
   229    if (remain.size() == 6) {
   230      xa_state.devdir_script = remain[4];
   231      xa_state.sdkroot_script = remain[5];
   232    } else {
   233      xa_state.devdir_script = "";
   234      xa_state.sdkroot_script = "";
   235    }
   236  
   237    kythe::ExtractorConfiguration config;
   238    config.SetPathCanonizalizationPolicy(
   239        absl::GetFlag(FLAGS_canonicalize_vname_paths));
   240    bool success = LoadExtraAction(xa_state, config);
   241    if (success) {
   242      config.Extract(kythe::supported_language::Language::kObjectiveC);
   243    } else {
   244      LOG(ERROR) << "Couldn't load extra action";
   245      // If we couldn't extract, just write an empty output file. This way the
   246      // extra_action will be a success from bazel's perspective, which should
   247      // remove some log spam.
   248      auto F = fopen(xa_state.output_file.c_str(), "w");
   249      if (F != nullptr) {
   250        fclose(F);
   251      }
   252    }
   253    google::protobuf::ShutdownProtobufLibrary();
   254    return 0;
   255  }