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 }