int main(int argc, char **argv) { std::vector<std::string> args = std::vector<std::string>(argv + 1, argv + argc); /* * Parse out the options, or print help & exit. */ Options options; std::pair<bool, std::string> result = libutil::Options::Parse<Options>(&options, args); if (!result.first) { return Help(result.second); } /* * Handle the basic options that don't need SDKs. */ if (options.tool().empty()) { if (options.help()) { return Help(); } else if (options.version()) { return Version(); } } /* * Parse fallback options from the environment. */ std::string toolchainsInput = options.toolchain(); if (toolchainsInput.empty()) { if (char const *toolchains = getenv("TOOLCHAINS")) { toolchainsInput = std::string(toolchains); } } std::string SDK = options.SDK(); if (SDK.empty()) { if (char const *sdkroot = getenv("SDKROOT")) { SDK = std::string(sdkroot); } else { /* Default SDK. */ SDK = "macosx"; } } bool verbose = options.verbose() || getenv("xcrun_verbose") != NULL; bool log = options.log() || getenv("xcrun_log") != NULL; bool nocache = options.noCache() || getenv("xcrun_nocache") != NULL; /* * Warn about unhandled arguments. */ if (nocache || options.killCache()) { fprintf(stderr, "warning: cache options not implemented\n"); } /* * Create filesystem. */ auto filesystem = std::unique_ptr<Filesystem>(new DefaultFilesystem()); /* * Load the SDK manager from the developer root. */ ext::optional<std::string> developerRoot = xcsdk::Environment::DeveloperRoot(filesystem.get()); if (!developerRoot) { fprintf(stderr, "error: unable to find developer root\n"); return -1; } auto configuration = xcsdk::Configuration::Load(filesystem.get(), xcsdk::Configuration::DefaultPaths()); auto manager = xcsdk::SDK::Manager::Open(filesystem.get(), *developerRoot, configuration); if (manager == nullptr) { fprintf(stderr, "error: unable to load manager from '%s'\n", developerRoot->c_str()); return -1; } if (verbose) { fprintf(stderr, "verbose: using developer root '%s'\n", manager->path().c_str()); } /* * Determine the SDK to use. */ xcsdk::SDK::Target::shared_ptr target = manager->findTarget(SDK); if (target == nullptr) { fprintf(stderr, "error: unable to find sdk '%s'\n", SDK.c_str()); return -1; } if (verbose) { fprintf(stderr, "verbose: using sdk '%s': %s\n", target->canonicalName().c_str(), target->path().c_str()); } /* * Determine the toolchains to use. Default to the SDK's toolchains. */ xcsdk::SDK::Toolchain::vector toolchains; if (!toolchainsInput.empty()) { /* If the custom toolchain exists, use it instead. */ std::vector<std::string> toolchainTokens = pbxsetting::Type::ParseList(toolchainsInput); for (std::string const &toolchainToken : toolchainTokens) { if (auto TC = manager->findToolchain(toolchainToken)) { toolchains.push_back(TC); } } if (toolchains.empty()) { fprintf(stderr, "error: unable to find toolchains in '%s'\n", toolchainsInput.c_str()); return -1; } } else { toolchains = target->toolchains(); } if (toolchains.empty()) { fprintf(stderr, "error: unable to find any toolchains\n"); return -1; } if (verbose) { fprintf(stderr, "verbose: using toolchain(s):"); for (xcsdk::SDK::Toolchain::shared_ptr const &toolchain : toolchains) { fprintf(stderr, " '%s'", toolchain->identifier().c_str()); } fprintf(stderr, "\n"); } /* * Perform actions. */ if (options.showSDKPath()) { printf("%s\n", target->path().c_str()); return 0; } else if (options.showSDKVersion()) { printf("%s\n", target->path().c_str()); return 0; } else if (options.showSDKBuildVersion()) { if (auto product = target->product()) { printf("%s\n", product->buildVersion().c_str()); return 0; } else { fprintf(stderr, "error: sdk has no build version\n"); return -1; } } else if (options.showSDKPlatformPath()) { if (auto platform = target->platform()) { printf("%s\n", platform->path().c_str()); } else { fprintf(stderr, "error: sdk has no platform\n"); return -1; } } else if (options.showSDKPlatformVersion()) { if (auto platform = target->platform()) { printf("%s\n", platform->version().c_str()); } else { fprintf(stderr, "error: sdk has no platform\n"); return -1; } } else { if (options.tool().empty()) { return Help("no tool provided"); } /* * Collect search paths for the tool. Can be in toolchains, target, developer root, or default paths. */ std::vector<std::string> executablePaths = target->executablePaths(toolchains); std::vector<std::string> defaultExecutablePaths = FSUtil::GetExecutablePaths(); executablePaths.insert(executablePaths.end(), defaultExecutablePaths.begin(), defaultExecutablePaths.end()); /* * Find the tool to execute. */ ext::optional<std::string> executable = filesystem->findExecutable(options.tool(), executablePaths); if (!executable) { fprintf(stderr, "error: tool '%s' not found\n", options.tool().c_str()); return 1; } if (verbose) { fprintf(stderr, "verbose: resolved tool '%s' to: %s\n", options.tool().c_str(), executable->c_str()); } if (options.find()) { /* * Just find the tool; i.e. print its path. */ printf("%s\n", executable->c_str()); return 0; } else { /* Run is the default. */ /* * Update effective environment to include the target path. */ std::unordered_map<std::string, std::string> environment = SysUtil::EnvironmentVariables(); environment["SDKROOT"] = target->path(); if (log) { printf("env SDKROOT=%s %s\n", target->path().c_str(), executable->c_str()); } /* * Execute the process! */ if (verbose) { printf("verbose: executing tool: %s\n", executable->c_str()); } Subprocess process; if (!process.execute(*executable, options.args(), environment)) { fprintf(stderr, "error: unable to execute tool '%s'\n", options.tool().c_str()); return -1; } return process.exitcode(); } } }