Exemplo n.º 1
0
int Driver::
run(std::vector<std::string> const &args, std::unordered_map<std::string, std::string> const &environment, Filesystem *filesystem, std::string const &workingDirectory)
{
    Options options;
    std::pair<bool, std::string> result = libutil::Options::Parse<Options>(&options, args);
    if (!result.first) {
        fprintf(stderr, "error: %s\n", result.second.c_str());
        return 1;
    }

    /* Validate options. */
    if (!options.input()) {
        fprintf(stderr, "error: no input file specified\n");
        return 1;
    }

    if (!options.output()) {
        fprintf(stderr, "error: no output file specified\n");
        return 1;
    }

    pbxsetting::Environment settingsEnvironment = CreateBuildEnvironment(environment);

    /* Read in the input. */
    std::vector<uint8_t> inputContents;
    if (!filesystem->read(&inputContents, FSUtil::ResolveRelativePath(*options.input(), workingDirectory))) {
        fprintf(stderr, "error: unable to read input %s\n", options.input()->c_str());
        return 1;
    }

    /* Determine the input format. */
    std::unique_ptr<plist::Format::Any> inputFormat = plist::Format::Any::Identify(inputContents);
    if (inputFormat == nullptr) {
        fprintf(stderr, "error: input %s is not a plist\n", options.input()->c_str());
        return 1;
    }

    /* Deserialize the input. */
    auto deserialize = plist::Format::Any::Deserialize(inputContents, *inputFormat);
    if (!deserialize.first) {
        fprintf(stderr, "error: %s: %s\n", options.input()->c_str(), deserialize.second.c_str());
        return 1;
    }

    plist::Dictionary *root = plist::CastTo<plist::Dictionary>(deserialize.first.get());
    if (root == nullptr) {
        fprintf(stderr, "error: info plist root is not a dictionary\n");
        return 1;
    }

    /*
     * Expand all build settings in the plist. Build settings can be in dictionary keys
     * and strings. The resolved build setting values are passed through the environment.
     */
    if (options.expandBuildSettings()) {
        ExpandBuildSettings(root, settingsEnvironment);
    }

    /*
     * Process additional content files. These are plists that get merged with the
     * main Info.plist at the top level.
     */
    for (std::string const &additionalContentFile : options.additionalContentFiles()) {
        std::vector<uint8_t> contents;
        if (!filesystem->read(&contents, FSUtil::ResolveRelativePath(additionalContentFile, workingDirectory))) {
            fprintf(stderr, "error: unable to read additional content file: %s\n", additionalContentFile.c_str());
            return 1;
        }

        auto additionalContent = plist::Format::Any::Deserialize(contents);
        if (additionalContent.first == nullptr) {
            fprintf(stderr, "error: unable to parse additional content file %s: %s\n", additionalContentFile.c_str(), additionalContent.second.c_str());
            return 1;
        }

        if (plist::Dictionary *dictionary = plist::CastTo<plist::Dictionary>(additionalContent.first.get())) {
            /* Pass true to replace existing entries. */
            root->merge(dictionary, true);
        }
    }

    /*
     * Info file keys/values: it's unknown what these are used for. Just warn.
     */
    if (options.infoFileKeys() || options.infoFileValues()) {
        // TODO(grp): Handle info file keys and values.
        fprintf(stderr, "warning: info file keys and values are not yet implemented\n");
    }

    /*
     * Platform and required architectures: it's unknown what these are used for. Just warn.
     */
    if (options.platform() || !options.requiredArchitectures().empty()) {
        // TODO(grp): Handle platform and required architectures.
#if 0
        fprintf(stderr, "warning: platform and required architectures are not yet implemented\n");
#endif
    }

    /*
     * Add entries to the Info.plist from the build environment.
     */
    AddBuildEnvironment(root, settingsEnvironment);

    /*
     * Write the PkgInfo file. This is just the package type and signature.
     */
    if (options.genPkgInfo()) {
        auto result = WritePkgInfo(filesystem, root, FSUtil::ResolveRelativePath(*options.genPkgInfo(), workingDirectory));
        if (!result.first) {
            fprintf(stderr, "error: %s\n", result.second.c_str());
            return 1;
        }
    }

    /*
     * Copy the resource rules file. This is used by code signing.
     */
    if (options.resourceRulesFile()) {
        std::string resourceRulesInputPath = settingsEnvironment.resolve("CODE_SIGN_RESOURCE_RULES_PATH");
        if (!resourceRulesInputPath.empty()) {
            std::vector<uint8_t> contents;
            if (!filesystem->read(&contents, FSUtil::ResolveRelativePath(resourceRulesInputPath, workingDirectory))) {
                fprintf(stderr, "error: unable to read input %s\n", resourceRulesInputPath.c_str());
                return 1;
            }

            if (!filesystem->write(contents, FSUtil::ResolveRelativePath(*options.resourceRulesFile(), workingDirectory))) {
                fprintf(stderr, "error: could not open output path %s to write\n", options.resourceRulesFile()->c_str());
                return 1;
            }
        }
    }

    /*
     * Determine the output format. By default, use the same as the input format.
     */
    plist::Format::Any outputFormat = *inputFormat;
    if (options.format()) {
        if (*options.format() == "binary") {
            outputFormat = plist::Format::Any::Create(plist::Format::Binary::Create());
        } else if (*options.format() == "xml") {
            outputFormat = plist::Format::Any::Create(plist::Format::XML::Create(plist::Format::Encoding::UTF8));
        } else if (*options.format() == "ascii" || *options.format() == "openstep") {
            outputFormat = plist::Format::Any::Create(plist::Format::ASCII::Create(false, plist::Format::Encoding::UTF8));
        } else {
            fprintf(stderr, "error: unknown output format %s\n", options.format()->c_str());
            return 1;
        }
    }

    /* Serialize the output. */
    auto serialize = plist::Format::Any::Serialize(root, outputFormat);
    if (serialize.first == nullptr) {
        fprintf(stderr, "error: %s\n", serialize.second.c_str());
        return 1;
    }

    /* Write out the output. */
    if (!filesystem->write(*serialize.first, FSUtil::ResolveRelativePath(*options.output(), workingDirectory))) {
        fprintf(stderr, "error: could not open output path %s to write\n", options.output()->c_str());
        return 1;
    }

    return 0;
}