// 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 com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.vfs.PathFragment; /** * Represents an .xcdatamodel[d] directory - knowing all {@code Artifact}s contained therein - and * the .zip file that it is compiled to which should be merged with the final application bundle. * <p> * An .xcdatamodel (here and below note that lack or presence of a d) directory contains the schema * for a managed object, or a managed object model. It typically has two files: {@code layout} and * {@code contents}, although this detail isn't addressed in Bazel code. Directories of this * sort are compiled into a single .mom file. If the .xcdatamodel directory is inside a * .xcdatamodeld directory, then the .mom file is placed inside a .momd directory. The .momd * directory or .mom file is placed in the bundle root of the final bundle. * <p> * An .xcdatamodeld directory contains several .xcdatamodel directories, each corresponding to a * different version. In addition the .xcdatamodeld directory contains a {@code .xccurrentversion} * file which identifies the current version. (this file is also not handled explicitly by Bazel * code). * <p> * When processing artifacts referenced by a {@code datamodels} attribute, we must determine if it * is in a .xcdatamodeld directory or only a .xcdatamodel directory. We also must group the * artifacts by their container, the container being an .xcdatamodeld directory if possible, and a * .xcdatamodel directory otherwise. Every container is compiled with a single invocation of the * Managed Object Model Compiler (momc) and corresponds to exactly one instance of this class. We * invoke momc indirectly through the momczip tool (part of Bazel) which runs momc and zips the * output. The files in this zip are placed in the bundle root of the final application, not unlike * the zips generated by {@code actoolzip} or {@code ibtoolzip}. */ class Xcdatamodel extends Value<Xcdatamodel> { private final Artifact outputZip; private final ImmutableSet<Artifact> inputs; private final PathFragment container; Xcdatamodel(Artifact outputZip, ImmutableSet<Artifact> inputs, PathFragment container) { super(ImmutableMap.of( "outputZip", outputZip, "inputs", inputs, "container", container)); this.outputZip = outputZip; this.inputs = inputs; this.container = container; } /** * Returns the files that should be supplied to Xcodegen when generating a project that includes * all of the given xcdatamodel source files. */ public static Iterable<Artifact> inputsToXcodegen(Iterable<Artifact> datamodelFiles) { ImmutableSet.Builder<Artifact> inputs = new ImmutableSet.Builder<>(); for (Artifact generalInput : datamodelFiles) { if (generalInput.getExecPath().getBaseName().equals(".xccurrentversion")) { inputs.add(generalInput); } } return inputs.build(); } public Artifact getOutputZip() { return outputZip; } /** * Returns every known file in the container. This is every input file that is processed by momc. */ public ImmutableSet<Artifact> getInputs() { return inputs; } public PathFragment getContainer() { return container; } /** * The ARCHIVE_ROOT passed to momczip. The archive root is the name of the .mom file * unversioned object models, and the name of the .momd directory for versioned object models. */ public String archiveRootForMomczip() { return name() + (container.getBaseName().endsWith(".xcdatamodeld") ? ".momd" : ".mom"); } /** * The name of the data model. This is the name of the container without the extension. For * instance, if the container is "foo/Information.xcdatamodel" or "bar/Information.xcdatamodeld", * then the name is "Information". */ public String name() { String baseContainerName = container.getBaseName(); int lastDot = baseContainerName.lastIndexOf('.'); return baseContainerName.substring(0, lastDot); } public static Iterable<Artifact> outputZips(Iterable<Xcdatamodel> models) { return Iterables.transform(models, new Function<Xcdatamodel, Artifact>() { @Override public Artifact apply(Xcdatamodel model) { return model.getOutputZip(); } }); } }