| // Copyright 2014 The Bazel Authors. All rights reserved. |
| // |
| // 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.google.devtools.build.lib.rules.objc; |
| |
| import static com.google.devtools.build.lib.rules.objc.ArtifactListAttribute.BUNDLE_IMPORTS; |
| import static com.google.devtools.build.lib.rules.objc.ObjcCommon.BUNDLE_CONTAINER_TYPE; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.devtools.build.xcode.bundlemerge.proto.BundleMergeProtos.BundleFile; |
| |
| /** |
| * Represents a file which is processed to another file and bundled. It contains the |
| * {@code Artifact} corresponding to the original file as well as the {@code Artifact} for the file |
| * converted to its bundled form. Examples of files that fit this pattern are .strings and .xib |
| * files. |
| */ |
| public final class BundleableFile extends Value<BundleableFile> { |
| |
| static final int EXECUTABLE_EXTERNAL_FILE_ATTRIBUTE = 0100755 << 16; |
| static final int DEFAULT_EXTERNAL_FILE_ATTRIBUTE = 0100644 << 16; |
| |
| /** The field in the Skylark struct that holds the {@code bundled} artifact. */ |
| static final String BUNDLED_FIELD = "file"; |
| |
| /** The field in the Skylark struct that holds the {@code bundlePath} string. */ |
| static final String BUNDLE_PATH_FIELD = "bundle_path"; |
| |
| private final Artifact bundled; |
| private final String bundlePath; |
| private final int zipExternalFileAttribute; |
| |
| /** |
| * Creates an instance whose {@code zipExternalFileAttribute} value is |
| * {@link #DEFAULT_EXTERNAL_FILE_ATTRIBUTE}. |
| */ |
| BundleableFile(Artifact bundled, String bundlePath) { |
| this(bundled, bundlePath, DEFAULT_EXTERNAL_FILE_ATTRIBUTE); |
| } |
| |
| /** |
| * @param bundled the {@link Artifact} whose data is placed in the bundle |
| * @param bundlePath the path of the file in the bundle |
| * @param zipExternalFileAttribute external file attribute of the file in the central directory of |
| * the bundle (zip file). The lower 16 bits contain the MS-DOS file attributes. The upper 16 |
| * bits contain the Unix file attributes, for instance 0100755 (octal) for a regular file with |
| * permissions {@code rwxr-xr-x}. |
| */ |
| BundleableFile(Artifact bundled, String bundlePath, int zipExternalFileAttribute) { |
| super(new ImmutableMap.Builder<String, Object>() |
| .put("bundled", bundled) |
| .put("bundlePath", bundlePath) |
| .put("zipExternalFileAttribute", zipExternalFileAttribute) |
| .build()); |
| this.bundled = bundled; |
| this.bundlePath = bundlePath; |
| this.zipExternalFileAttribute = zipExternalFileAttribute; |
| } |
| |
| static String flatBundlePath(PathFragment path) { |
| String containingDir = path.getParentDirectory().getBaseName(); |
| return (containingDir.endsWith(".lproj") ? (containingDir + "/") : "") + path.getBaseName(); |
| } |
| |
| /** |
| * Given a sequence of non-compiled resource files, returns a sequence of the same length of |
| * instances of this class with the resource paths flattened (resources are put in the bundle |
| * root) or, if the source file is in a directory ending in {@code .lproj}, in a directory of that |
| * name directly under the bundle root. |
| * |
| * <p>Non-compiled resource files are resources which are not processed before placing them in the |
| * final bundle. This is different from (for example) {@code .strings} and {@code .xib} files, |
| * which must be converted to binary plist form or compiled. |
| * |
| * @param files a sequence of artifacts corresponding to non-compiled resource files |
| */ |
| public static Iterable<BundleableFile> flattenedRawResourceFiles(Iterable<Artifact> files) { |
| ImmutableList.Builder<BundleableFile> result = new ImmutableList.Builder<>(); |
| for (Artifact file : files) { |
| result.add(new BundleableFile(file, flatBundlePath(file.getExecPath()))); |
| } |
| return result.build(); |
| } |
| |
| /** |
| * Given a sequence of non-compiled resource files, returns a sequence of the same length of |
| * instances of this class with the resource paths copied as-is into the bundle root. |
| * |
| * <p>Non-compiled resource files are resources which are not processed before placing them in the |
| * final bundle. This is different from (for example) {@code .strings} and {@code .xib} files, |
| * which must be converted to binary plist form or compiled. |
| * |
| * @param files a sequence of artifacts corresponding to non-compiled resource files |
| */ |
| public static Iterable<BundleableFile> structuredRawResourceFiles(Iterable<Artifact> files) { |
| ImmutableList.Builder<BundleableFile> result = new ImmutableList.Builder<>(); |
| for (Artifact file : files) { |
| result.add(new BundleableFile(file, ownerBundlePath(file))); |
| } |
| return result.build(); |
| } |
| |
| private static String ownerBundlePath(Artifact file) { |
| PathFragment packageFragment = |
| file.getArtifactOwner().getLabel().getPackageIdentifier().getSourceRoot(); |
| return file.getRootRelativePath().relativeTo(packageFragment).toString(); |
| } |
| |
| /** |
| * Returns an instance for every file in a bundle directory. |
| * <p> |
| * This uses the parent-most container matching {@code *.bundle} as the bundle root. |
| * TODO(bazel-team): add something like an import_root attribute to specify this explicitly, which |
| * will be helpful if a bundle that appears to be nested needs to be imported alone. |
| */ |
| public static Iterable<BundleableFile> bundleImportsFromRule(RuleContext context) { |
| ImmutableList.Builder<BundleableFile> result = new ImmutableList.Builder<>(); |
| for (Artifact artifact : BUNDLE_IMPORTS.get(context)) { |
| for (PathFragment container : |
| ObjcCommon.farthestContainerMatching(BUNDLE_CONTAINER_TYPE, artifact).asSet()) { |
| // TODO(bazel-team): Figure out if we need to remove symbols of architectures we aren't |
| // building for from the binary in the bundle. |
| result.add(new BundleableFile( |
| artifact, |
| // The path from the artifact's container (including the container), to the artifact |
| // itself. For instance, if artifact is foo/bar.bundle/baz, then this value |
| // is bar.bundle/baz. |
| artifact.getExecPath().relativeTo(container.getParentDirectory()).getSafePathString())); |
| } |
| } |
| return result.build(); |
| } |
| |
| /** |
| * Returns the location into which this file should be put in, relative to the bundle root. |
| */ |
| public String getBundlePath() { |
| return bundlePath; |
| } |
| |
| /** |
| * Returns the artifact representing the source for this bundleable file. |
| */ |
| public Artifact getBundled() { |
| return bundled; |
| } |
| |
| /** |
| * Returns bundle files for each given strings file. These are used to merge the strings files to |
| * the final application bundle. |
| */ |
| public static Iterable<BundleFile> toBundleFiles(Iterable<BundleableFile> files) { |
| ImmutableList.Builder<BundleFile> result = new ImmutableList.Builder<>(); |
| for (BundleableFile file : files) { |
| result.add(BundleFile.newBuilder() |
| .setBundlePath(file.bundlePath) |
| .setSourceFile(file.bundled.getExecPathString()) |
| .setExternalFileAttribute(file.zipExternalFileAttribute) |
| .build()); |
| } |
| return result.build(); |
| } |
| |
| /** |
| * Returns the artifacts for the bundled files. These can be used, for instance, as the input |
| * files to add to the bundlemerge action for a bundle that contains all the given files. |
| */ |
| public static Iterable<Artifact> toArtifacts(Iterable<BundleableFile> files) { |
| ImmutableList.Builder<Artifact> result = new ImmutableList.Builder<>(); |
| for (BundleableFile file : files) { |
| result.add(file.bundled); |
| } |
| return result.build(); |
| } |
| } |