| /* |
| * Copyright 2016 The Kythe Authors. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // objc_extractor_bazel is a Objective-C extractor meant to be run as a Bazel |
| // extra_action. It may be used with third_party/bazel/get_devdir.sh and |
| // third_party/bazel/get_sdkroot.sh to fix placeholders left in arguments by |
| // Bazel. |
| // |
| // For example: |
| // |
| // action_listener( |
| // name = "extract_kindex", |
| // extra_actions = [":extra_action"], |
| // mnemonics = ["ObjcCompile"], |
| // visibility = ["//visibility:public"], |
| // ) |
| // |
| // extra_action( |
| // name = "extra_action", |
| // cmd = "$(location :objc_extractor_binary) \ |
| // $(EXTRA_ACTION_FILE) \ |
| // $(output $(ACTION_ID).objc.kindex) \ |
| // $(location :vnames_config) \ |
| // $(location :get_devdir) \ |
| // $(location :get_sdkroot)", |
| // data = [ |
| // ":get_devdir", |
| // ":get_sdkroot", |
| // ":vnames_config", |
| // ], |
| // out_templates = ["$(ACTION_ID).objc.kindex"], |
| // tools = [":objc_extractor_binary"], |
| // ) |
| // |
| // # In this example, the extractor binary is pre-built. |
| // filegroup( |
| // name = "objc_extractor_binary", |
| // srcs = ["objc_extractor_bazel"], |
| // ) |
| // |
| // filegroup( |
| // name = "vnames_config", |
| // srcs = ["vnames.json"], |
| // ) |
| // |
| // sh_binary( |
| // name = "get_devdir", |
| // srcs = ["get_devdir.sh"], |
| // ) |
| // |
| // sh_binary( |
| // name = "get_sdkroot", |
| // srcs = ["get_sdkroot.sh"], |
| // ) |
| |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include "absl/strings/str_format.h" |
| #include "cxx_extractor.h" |
| #include "gflags/gflags.h" |
| #include "glog/logging.h" |
| #include "google/protobuf/io/coded_stream.h" |
| #include "google/protobuf/io/zero_copy_stream.h" |
| #include "google/protobuf/io/zero_copy_stream_impl.h" |
| #include "google/protobuf/stubs/common.h" |
| #include "kythe/cxx/extractor/language.h" |
| #include "objc_bazel_support.h" |
| #include "third_party/bazel/src/main/protobuf/extra_actions_base.pb.h" |
| |
| struct XAState { |
| std::string extra_action_file; |
| std::string output_file; |
| std::string vname_config; |
| std::string devdir_script; |
| std::string sdkroot_script; |
| }; |
| |
| static bool ContainsUnsupportedArg(const std::vector<std::string>& args) { |
| for (const auto& arg : args) { |
| // We do not support compilations using modules yet. |
| if (arg == "-fmodules") { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static bool LoadSpawnInfo(const XAState& xa_state, |
| const blaze::ExtraActionInfo& info, |
| kythe::ExtractorConfiguration& config) { |
| blaze::SpawnInfo spawn_info = info.GetExtension(blaze::SpawnInfo::spawn_info); |
| |
| std::vector<std::string> args; |
| // If the user didn't specify a script path, don't mutate the arguments in the |
| // extra action. |
| if (xa_state.devdir_script.empty() || xa_state.sdkroot_script.empty()) { |
| for (const auto& i : spawn_info.argument()) { |
| std::string arg = i; |
| args.push_back(arg); |
| } |
| } else { |
| auto cmdPrefix = kythe::BuildEnvVarCommandPrefix(spawn_info.variable()); |
| auto devdir = kythe::RunScript(cmdPrefix + xa_state.devdir_script); |
| auto sdkroot = kythe::RunScript(cmdPrefix + xa_state.sdkroot_script); |
| |
| kythe::FillWithFixedArgs(args, spawn_info, devdir, sdkroot); |
| } |
| |
| if (ContainsUnsupportedArg(args)) { |
| LOG(INFO) << "Not extracting " << info.owner() |
| << " because it had an unsupported argument."; |
| return false; |
| } |
| |
| config.SetOutputFile(xa_state.output_file); |
| config.SetArgs(args); |
| config.SetVNameConfig(xa_state.vname_config); |
| config.SetTargetName(info.owner()); |
| if (spawn_info.output_file_size() > 0) { |
| config.SetCompilationOutputPath(spawn_info.output_file(0)); |
| } |
| return true; |
| } |
| |
| static bool LoadCppInfo(const XAState& xa_state, |
| const blaze::ExtraActionInfo& info, |
| kythe::ExtractorConfiguration& config) { |
| blaze::CppCompileInfo cpp_info = |
| info.GetExtension(blaze::CppCompileInfo::cpp_compile_info); |
| |
| std::vector<std::string> args; |
| // If the user didn't specify a script path, don't mutate the arguments in the |
| // extra action. |
| if (xa_state.devdir_script.empty() || xa_state.sdkroot_script.empty()) { |
| args.push_back(cpp_info.tool()); |
| for (const auto& i : cpp_info.compiler_option()) { |
| std::string arg = i; |
| args.push_back(arg); |
| } |
| } else { |
| auto cmdPrefix = kythe::BuildEnvVarCommandPrefix(cpp_info.variable()); |
| auto devdir = kythe::RunScript(cmdPrefix + xa_state.devdir_script); |
| auto sdkroot = kythe::RunScript(cmdPrefix + xa_state.sdkroot_script); |
| |
| kythe::FillWithFixedArgs(args, cpp_info, devdir, sdkroot); |
| } |
| |
| if (ContainsUnsupportedArg(args)) { |
| LOG(INFO) << "Not extracting " << info.owner() |
| << " because it had an unsupported argument."; |
| return false; |
| } |
| |
| config.SetOutputFile(xa_state.output_file); |
| config.SetArgs(args); |
| config.SetVNameConfig(xa_state.vname_config); |
| config.SetTargetName(info.owner()); |
| config.SetCompilationOutputPath(cpp_info.output_file()); |
| return true; |
| } |
| |
| static bool LoadExtraAction(const XAState& xa_state, |
| kythe::ExtractorConfiguration& config) { |
| using namespace google::protobuf::io; |
| blaze::ExtraActionInfo info; |
| int fd = |
| open(xa_state.extra_action_file.c_str(), O_RDONLY, S_IREAD | S_IWRITE); |
| CHECK_GE(fd, 0) << "Couldn't open input file " << xa_state.extra_action_file; |
| FileInputStream file_input_stream(fd); |
| CodedInputStream coded_input_stream(&file_input_stream); |
| coded_input_stream.SetTotalBytesLimit(INT_MAX); |
| CHECK(info.ParseFromCodedStream(&coded_input_stream)); |
| close(fd); |
| |
| if (info.HasExtension(blaze::SpawnInfo::spawn_info)) { |
| return LoadSpawnInfo(xa_state, info, config); |
| } else if (info.HasExtension(blaze::CppCompileInfo::cpp_compile_info)) { |
| return LoadCppInfo(xa_state, info, config); |
| } |
| LOG(ERROR) |
| << "ObjcCompile Extra Action didn't have SpawnInfo or CppCompileInfo."; |
| return false; |
| } |
| |
| int main(int argc, char* argv[]) { |
| GOOGLE_PROTOBUF_VERIFY_VERSION; |
| google::InitGoogleLogging(argv[0]); |
| gflags::SetVersionString("0.2"); |
| if (argc != 4 && argc != 6) { |
| absl::FPrintF( |
| stderr, |
| "Invalid number of arguments:\n\tCall as %s extra-action-file " |
| "output-file vname-config [devdir-script sdkroot-script]\n", |
| argv[0]); |
| return 1; |
| } |
| XAState xa_state; |
| xa_state.extra_action_file = argv[1]; |
| xa_state.output_file = argv[2]; |
| xa_state.vname_config = argv[3]; |
| if (argc == 6) { |
| xa_state.devdir_script = argv[4]; |
| xa_state.sdkroot_script = argv[5]; |
| } else { |
| xa_state.devdir_script = ""; |
| xa_state.sdkroot_script = ""; |
| } |
| |
| kythe::ExtractorConfiguration config; |
| bool success = LoadExtraAction(xa_state, config); |
| if (success) { |
| config.Extract(kythe::supported_language::Language::kObjectiveC); |
| } else { |
| // If we couldn't extract, just write an empty output file. This way the |
| // extra_action will be a success from bazel's perspective, which should |
| // remove some log spam. |
| auto F = fopen(xa_state.output_file.c_str(), "w"); |
| if (F != nullptr) { |
| fclose(F); |
| } |
| } |
| google::protobuf::ShutdownProtobufLibrary(); |
| return 0; |
| } |