Exemplo n.º 1
0
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();
        }
    }
}