com.facebook.buck.json.BuckPythonProgram.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.json.BuckPythonProgram.java

Source

/*
 * Copyright 2016-present Facebook, Inc.
 *
 * 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.
 */
package com.facebook.buck.json;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.facebook.buck.io.MoreFiles;
import com.facebook.buck.io.MorePaths;
import com.facebook.buck.log.Logger;
import com.facebook.buck.rules.BuckPyFunction;
import com.facebook.buck.rules.ConstructorArgMarshaller;
import com.facebook.buck.rules.Description;
import com.facebook.buck.util.Escaper;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Resources;

import java.io.IOException;
import java.io.Writer;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * Represents a serialized copy of the buck python program used to read BUCK files.
 * <p/>
 * Layout of the directory:
 * <pre>
 *  root/
 *    __main__.py
 *    generated_rules.py
 *    python_bundle.zip
 * </pre>
 */
class BuckPythonProgram implements AutoCloseable {

    private static final Path PATH_TO_PATHLIB_PY = Paths
            .get(System.getProperty("buck.path_to_pathlib_py", "third-party/py/pathlib/pathlib.py"));

    private static final Path PATH_TO_PYWATCHMAN = Paths
            .get(System.getProperty("buck.path_to_pywatchman", "third-party/py/pywatchman"));

    private static final Logger LOG = Logger.get(BuckPythonProgram.class);

    private final Path rootDirectory;

    /**
     * Create a new instance by layout the files in a temporary directory.
     */
    public static BuckPythonProgram newInstance(ConstructorArgMarshaller marshaller,
            ImmutableSet<Description<?>> descriptions) throws IOException {

        Path pythonPath;

        try {
            URL url = Resources.getResource("buck_parser");

            if ("jar".equals(url.getProtocol())) {
                // Buck is being executed from a JAR file. Extract the jar file from the resource path, and
                // verify it is correct.
                // When python attempts to import `buck_parser`, it will see the jar file, and load it via
                // zipimport, and look into the `buck_parser` directory in the root of the jar.
                JarURLConnection connection = (JarURLConnection) url.openConnection();
                Preconditions.checkState(connection.getEntryName().equals("buck_parser"),
                        "buck_parser directory should be at the root of the jar file.");
                URI jarFileURI = connection.getJarFileURL().toURI();
                pythonPath = Paths.get(jarFileURI);
            } else if ("file".equals(url.getProtocol())) {
                // Buck is being executed from classpath on disk. Set the parent directory as the python
                // path.
                // When python attempts to import `buck_parser`, it will look for a `buck_parser` child
                // directory in the given path.
                pythonPath = Paths.get(url.toURI()).getParent();
            } else {
                throw new IllegalStateException(
                        "buck_python resource directory should reside in a local directory or in a jar file. "
                                + "Got: " + url);
            }
        } catch (URISyntaxException e) {
            throw new IllegalStateException("Failed to determine location of buck_parser python package", e);
        }

        Path generatedRoot = Files.createTempDirectory("buck_python_program");
        LOG.debug("Writing python rules stub to %s.", generatedRoot);
        try (Writer out = Files.newBufferedWriter(generatedRoot.resolve("generated_rules.py"), UTF_8)) {
            out.write("from buck_parser.buck import *\n\n");
            BuckPyFunction function = new BuckPyFunction(marshaller);
            for (Description<?> description : descriptions) {
                out.write(function.toPythonFunction(Description.getBuildRuleType(description),
                        description.createUnpopulatedConstructorArg()));
                out.write('\n');
            }
        }

        String pathlibDir = PATH_TO_PATHLIB_PY.getParent().toString();
        String watchmanDir = PATH_TO_PYWATCHMAN.toString();
        try (Writer out = Files.newBufferedWriter(generatedRoot.resolve("__main__.py"), UTF_8)) {
            out.write(Joiner.on("\n").join("from __future__ import absolute_import", "import sys",
                    "sys.path.insert(0, \""
                            + Escaper.escapeAsBashString(MorePaths.pathWithUnixSeparators(pathlibDir)) + "\")",
                    "sys.path.insert(0, \""
                            + Escaper.escapeAsBashString(MorePaths.pathWithUnixSeparators(watchmanDir)) + "\")",
                    // Path to the bundled python code.
                    "sys.path.insert(0, \""
                            + Escaper.escapeAsBashString(MorePaths.pathWithUnixSeparators(pythonPath)) + "\")",
                    // Path to the generated rules stub.
                    "sys.path.insert(0, \""
                            + Escaper.escapeAsBashString(MorePaths.pathWithUnixSeparators(generatedRoot)) + "\")",
                    "if __name__ == '__main__':", "    try:", "        from buck_parser import buck",
                    "        buck.main()", "    except KeyboardInterrupt:",
                    "        print >> sys.stderr, 'Killed by User'", ""));
        }

        LOG.debug("Created temporary buck.py instance at %s.", generatedRoot);
        return new BuckPythonProgram(generatedRoot);
    }

    public Path getExecutablePath() {
        return this.rootDirectory.resolve("__main__.py");
    }

    @Override
    public void close() throws IOException {
        MoreFiles.deleteRecursively(this.rootDirectory);
    }

    private BuckPythonProgram(Path rootDirectory) {
        this.rootDirectory = rootDirectory;
    }
}