| // Copyright 2016 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.AppleSkylarkCommon.BAD_SET_TYPE_ERROR; |
| import static com.google.devtools.build.lib.rules.objc.AppleSkylarkCommon.MISSING_KEY_ERROR; |
| import static com.google.devtools.build.lib.rules.objc.AppleSkylarkCommon.NOT_SET_ERROR; |
| import static com.google.devtools.build.lib.rules.objc.BundleableFile.BUNDLED_FIELD; |
| import static com.google.devtools.build.lib.rules.objc.BundleableFile.BUNDLE_PATH_FIELD; |
| |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.packages.StructImpl; |
| import com.google.devtools.build.lib.packages.StructProvider; |
| import com.google.devtools.build.lib.rules.objc.ObjcProvider.Key; |
| import com.google.devtools.build.lib.syntax.EvalException; |
| import com.google.devtools.build.lib.syntax.EvalUtils; |
| import com.google.devtools.build.lib.syntax.SkylarkNestedSet; |
| import com.google.devtools.build.lib.syntax.SkylarkType; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| |
| /** |
| * A utility class for converting ObjcProvider values between java and skylark representation. |
| */ |
| public class ObjcProviderSkylarkConverters { |
| |
| /** |
| * A map of possible NestedSet types to the converters that should define their treatment |
| * in translating between a java and skylark ObjcProvider. |
| */ |
| private static final ImmutableMap<Class<?>, Converter> CONVERTERS = |
| ImmutableMap.<Class<?>, Converter>builder() |
| .put(Artifact.class, new DirectConverter()) |
| .put(String.class, new DirectConverter()) |
| .put(PathFragment.class, new PathFragmentToStringConverter()) |
| .put(SdkFramework.class, new SdkFrameworkToStringConverter()) |
| .put(BundleableFile.class, new BundleableFileToStructConverter()) |
| .build(); |
| |
| /** |
| * Returns a value for a skylark attribute given a java ObjcProvider key and value. |
| */ |
| public static Object convertToSkylark(Key<?> javaKey, NestedSet<?> javaValue) { |
| return CONVERTERS.get(javaKey.getType()).valueForSkylark(javaKey, javaValue); |
| } |
| |
| /** |
| * Returns a value for a java ObjcProvider given a key and a corresponding skylark value. |
| */ |
| public static Iterable<?> convertToJava(Key<?> javaKey, Object skylarkValue) { |
| return CONVERTERS.get(javaKey.getType()).valueForJava(javaKey, skylarkValue); |
| } |
| |
| /** |
| * Converts {@link PathFragment}s into a skylark-compatible nested set of path strings. |
| */ |
| public static SkylarkNestedSet convertPathFragmentsToSkylark( |
| Iterable<PathFragment> pathFragments) { |
| NestedSetBuilder<String> result = NestedSetBuilder.stableOrder(); |
| for (PathFragment path : pathFragments) { |
| result.add(path.getSafePathString()); |
| } |
| return SkylarkNestedSet.of(String.class, result.build()); |
| } |
| |
| /** |
| * A converter for ObjcProvider values. |
| */ |
| private static interface Converter { |
| /** |
| * Translates a java ObjcProvider value to a skylark ObjcProvider value. |
| */ |
| abstract Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue); |
| |
| /** |
| * Translates a skylark ObjcProvider value to a java ObjcProvider value. |
| */ |
| abstract Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue); |
| } |
| |
| /** |
| * A converter that uses the same value for java and skylark. |
| */ |
| private static class DirectConverter implements Converter { |
| |
| @Override |
| public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) { |
| SkylarkType type = SkylarkType.of(javaKey.getType()); |
| return SkylarkNestedSet.of(type, javaValue); |
| } |
| |
| @Override |
| public Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue) { |
| validateTypes(skylarkValue, javaKey.getType(), javaKey.getSkylarkKeyName()); |
| return ((SkylarkNestedSet) skylarkValue).toCollection(); |
| } |
| } |
| |
| /** |
| * A converter that that translates between a java PathFragment and a skylark string. |
| */ |
| private static class PathFragmentToStringConverter implements Converter { |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) { |
| return convertPathFragmentsToSkylark((NestedSet<PathFragment>) javaValue); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue) { |
| validateTypes(skylarkValue, String.class, javaKey.getSkylarkKeyName()); |
| NestedSetBuilder<PathFragment> result = NestedSetBuilder.stableOrder(); |
| for (String path : ((SkylarkNestedSet) skylarkValue).toCollection(String.class)) { |
| result.add(PathFragment.create(path)); |
| } |
| return result.build(); |
| } |
| } |
| |
| /** |
| * A converter that that translates between a java {@link SdkFramework} and a skylark string. |
| */ |
| private static class SdkFrameworkToStringConverter implements Converter { |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) { |
| NestedSetBuilder<String> result = NestedSetBuilder.stableOrder(); |
| for (SdkFramework framework : (Iterable<SdkFramework>) javaValue) { |
| result.add(framework.getName()); |
| } |
| return SkylarkNestedSet.of(String.class, result.build()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue) { |
| validateTypes(skylarkValue, String.class, javaKey.getSkylarkKeyName()); |
| NestedSetBuilder<SdkFramework> result = NestedSetBuilder.stableOrder(); |
| for (String path : ((SkylarkNestedSet) skylarkValue).toCollection(String.class)) { |
| result.add(new SdkFramework(path)); |
| } |
| return result.build(); |
| } |
| } |
| |
| /** |
| * A converter that that translates between a java BundleableFile and a skylark struct. |
| */ |
| private static class BundleableFileToStructConverter implements Converter { |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Object valueForSkylark(Key<?> javaKey, NestedSet<?> javaValue) { |
| NestedSetBuilder<StructImpl> result = NestedSetBuilder.stableOrder(); |
| for (BundleableFile bundleableFile : (Iterable<BundleableFile>) javaValue) { |
| result.add( |
| StructProvider.STRUCT.create( |
| ImmutableMap.<String, Object>of( |
| BUNDLED_FIELD, bundleableFile.getBundled(), |
| BUNDLE_PATH_FIELD, bundleableFile.getBundlePath()), |
| "No such attribute '%s'")); |
| } |
| return SkylarkNestedSet.of(StructImpl.class, result.build()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public Iterable<?> valueForJava(Key<?> javaKey, Object skylarkValue) { |
| validateTypes(skylarkValue, StructImpl.class, javaKey.getSkylarkKeyName()); |
| NestedSetBuilder<BundleableFile> result = NestedSetBuilder.stableOrder(); |
| for (StructImpl struct : ((SkylarkNestedSet) skylarkValue).toCollection(StructImpl.class)) { |
| Artifact artifact; |
| String path; |
| try { |
| artifact = struct.getValue(BUNDLED_FIELD, Artifact.class); |
| path = struct.getValue(BUNDLE_PATH_FIELD, String.class); |
| } catch (EvalException e) { |
| throw new IllegalArgumentException(e.getMessage()); |
| } |
| if (artifact == null) { |
| throw new IllegalArgumentException(String.format(MISSING_KEY_ERROR, BUNDLED_FIELD)); |
| } |
| if (path == null) { |
| throw new IllegalArgumentException(String.format(MISSING_KEY_ERROR, BUNDLE_PATH_FIELD)); |
| } |
| result.add(new BundleableFile(artifact, path)); |
| } |
| return result.build(); |
| } |
| } |
| |
| /** |
| * Throws an error if the given object is not a nested set of the given type. |
| */ |
| private static void validateTypes(Object toCheck, Class<?> expectedSetType, String keyName) { |
| if (!(toCheck instanceof SkylarkNestedSet)) { |
| throw new IllegalArgumentException( |
| String.format(NOT_SET_ERROR, keyName, EvalUtils.getDataTypeName(toCheck))); |
| } else if (!((SkylarkNestedSet) toCheck).getContentType().canBeCastTo(expectedSetType)) { |
| throw new IllegalArgumentException( |
| String.format( |
| BAD_SET_TYPE_ERROR, |
| keyName, |
| EvalUtils.getDataTypeNameFromClass(expectedSetType), |
| EvalUtils.getDataTypeNameFromClass( |
| ((SkylarkNestedSet) toCheck).getContentType().getType()))); |
| } |
| } |
| } |