// 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.Info; 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, Converter> CONVERTERS = ImmutableMap., 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 pathFragments) { NestedSetBuilder 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) javaValue); } @SuppressWarnings("unchecked") @Override public Iterable valueForJava(Key javaKey, Object skylarkValue) { validateTypes(skylarkValue, String.class, javaKey.getSkylarkKeyName()); NestedSetBuilder 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 result = NestedSetBuilder.stableOrder(); for (SdkFramework framework : (Iterable) 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 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 result = NestedSetBuilder.stableOrder(); for (BundleableFile bundleableFile : (Iterable) javaValue) { result.add( StructProvider.STRUCT.create( ImmutableMap.of( BUNDLED_FIELD, bundleableFile.getBundled(), BUNDLE_PATH_FIELD, bundleableFile.getBundlePath()), "No such attribute '%s'")); } return SkylarkNestedSet.of(Info.class, result.build()); } @SuppressWarnings("unchecked") @Override public Iterable valueForJava(Key javaKey, Object skylarkValue) { validateTypes(skylarkValue, Info.class, javaKey.getSkylarkKeyName()); NestedSetBuilder result = NestedSetBuilder.stableOrder(); for (Info struct : ((SkylarkNestedSet) skylarkValue).toCollection(Info.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()))); } } }