• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.turbine.deps;
18 
19 import com.google.common.base.Predicates;
20 import com.google.common.collect.Collections2;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.turbine.binder.Binder.BindingResult;
24 import com.google.turbine.binder.ClassPath;
25 import com.google.turbine.binder.bound.EnumConstantValue;
26 import com.google.turbine.binder.bound.TurbineAnnotationValue;
27 import com.google.turbine.binder.bound.TurbineClassValue;
28 import com.google.turbine.binder.bound.TypeBoundClass;
29 import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
30 import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
31 import com.google.turbine.binder.bytecode.BytecodeBoundClass;
32 import com.google.turbine.binder.env.CompoundEnv;
33 import com.google.turbine.binder.env.Env;
34 import com.google.turbine.binder.env.SimpleEnv;
35 import com.google.turbine.binder.sym.ClassSymbol;
36 import com.google.turbine.lower.Lower.Lowered;
37 import com.google.turbine.model.Const;
38 import com.google.turbine.proto.DepsProto;
39 import com.google.turbine.type.AnnoInfo;
40 import com.google.turbine.type.Type;
41 import java.io.BufferedInputStream;
42 import java.io.IOError;
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.nio.file.Files;
46 import java.nio.file.Paths;
47 import java.util.Collection;
48 import java.util.HashSet;
49 import java.util.LinkedHashSet;
50 import java.util.Optional;
51 import java.util.Set;
52 
53 /** Support for Bazel jdeps dependency output. */
54 public class Dependencies {
55   /** Creates a jdeps proto for the current compilation. */
collectDeps( Optional<String> targetLabel, ClassPath bootclasspath, BindingResult bound, Lowered lowered)56   public static DepsProto.Dependencies collectDeps(
57       Optional<String> targetLabel, ClassPath bootclasspath, BindingResult bound, Lowered lowered) {
58     DepsProto.Dependencies.Builder deps = DepsProto.Dependencies.newBuilder();
59     Set<ClassSymbol> closure = superTypeClosure(bound, lowered);
60     addPackageInfos(closure, bound);
61     Set<String> jars = new LinkedHashSet<>();
62     for (ClassSymbol sym : closure) {
63       BytecodeBoundClass info = bound.classPathEnv().get(sym);
64       if (info == null) {
65         // the symbol wasn't loaded from the classpath
66         continue;
67       }
68       String jarFile = info.jarFile();
69       if (bootclasspath.env().get(sym) != null) {
70         // bootclasspath deps are not tracked
71         continue;
72       }
73       jars.add(jarFile);
74     }
75     for (String jarFile : jars) {
76       deps.addDependency(
77           DepsProto.Dependency.newBuilder()
78               .setPath(jarFile)
79               .setKind(DepsProto.Dependency.Kind.EXPLICIT));
80     }
81     // we don't current write jdeps for failed compilations
82     deps.setSuccess(true);
83     if (targetLabel.isPresent()) {
84       deps.setRuleLabel(targetLabel.get());
85     }
86     return deps.build();
87   }
88 
superTypeClosure(BindingResult bound, Lowered lowered)89   private static Set<ClassSymbol> superTypeClosure(BindingResult bound, Lowered lowered) {
90     Env<ClassSymbol, TypeBoundClass> env =
91         CompoundEnv.<ClassSymbol, TypeBoundClass>of(new SimpleEnv<>(bound.units()))
92             .append(bound.classPathEnv());
93     Set<ClassSymbol> closure = new LinkedHashSet<>(lowered.symbols());
94     for (ClassSymbol sym : lowered.symbols()) {
95       TypeBoundClass info = env.get(sym);
96       addAnnotations(closure, info.annotations());
97       for (MethodInfo method : info.methods()) {
98         addAnnotations(closure, method.annotations());
99       }
100       for (FieldInfo field : info.fields()) {
101         addAnnotations(closure, field.annotations());
102       }
103       addSuperTypes(closure, env, info);
104     }
105     return closure;
106   }
107 
addAnnotations( Set<ClassSymbol> closure, ImmutableList<AnnoInfo> annotations)108   private static void addAnnotations(
109       Set<ClassSymbol> closure, ImmutableList<AnnoInfo> annotations) {
110     for (AnnoInfo annoInfo : annotations) {
111       addAnnotation(closure, annoInfo);
112     }
113   }
114 
addAnnotation(Set<ClassSymbol> closure, AnnoInfo annoInfo)115   private static void addAnnotation(Set<ClassSymbol> closure, AnnoInfo annoInfo) {
116     closure.add(annoInfo.sym());
117     for (Const c : annoInfo.values().values()) {
118       addConst(closure, c);
119     }
120   }
121 
addConst(Set<ClassSymbol> closure, Const c)122   private static void addConst(Set<ClassSymbol> closure, Const c) {
123     switch (c.kind()) {
124       case ARRAY:
125         for (Const e : ((Const.ArrayInitValue) c).elements()) {
126           addConst(closure, e);
127         }
128         break;
129       case CLASS_LITERAL:
130         Type t = ((TurbineClassValue) c).type();
131         if (t.tyKind() == Type.TyKind.CLASS_TY) {
132           closure.add(((Type.ClassTy) t).sym());
133         }
134         break;
135       case ENUM_CONSTANT:
136         closure.add(((EnumConstantValue) c).sym().owner());
137         break;
138       case ANNOTATION:
139         addAnnotation(closure, ((TurbineAnnotationValue) c).info());
140         break;
141       case PRIMITIVE:
142         // continue below
143     }
144   }
145 
addSuperTypes( Set<ClassSymbol> closure, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym)146   private static void addSuperTypes(
147       Set<ClassSymbol> closure, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym) {
148     if (!closure.add(sym)) {
149       return;
150     }
151     TypeBoundClass info = env.get(sym);
152     if (info == null) {
153       return;
154     }
155     addSuperTypes(closure, env, info);
156   }
157 
addSuperTypes( Set<ClassSymbol> closure, Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass info)158   private static void addSuperTypes(
159       Set<ClassSymbol> closure, Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass info) {
160     if (info.superclass() != null) {
161       addSuperTypes(closure, env, info.superclass());
162     }
163     for (ClassSymbol i : info.interfaces()) {
164       addSuperTypes(closure, env, i);
165     }
166   }
167 
addPackageInfos(Set<ClassSymbol> closure, BindingResult bound)168   private static void addPackageInfos(Set<ClassSymbol> closure, BindingResult bound) {
169     Set<ClassSymbol> packages = new LinkedHashSet<>();
170     for (ClassSymbol sym : closure) {
171       String packageName = sym.packageName();
172       if (packageName.isEmpty()) {
173         continue;
174       }
175       packages.add(new ClassSymbol(packageName + "/package-info"));
176     }
177     for (ClassSymbol pkg : packages) {
178       if (bound.classPathEnv().get(pkg) != null) {
179         closure.add(pkg);
180       }
181     }
182   }
183 
184   /**
185    * Filters a transitive classpath to contain only the entries for direct dependencies, and the
186    * types needed to compile those direct deps as reported by jdeps.
187    *
188    * <p>If no direct dependency information is available the full transitive classpath is returned.
189    */
reduceClasspath( ImmutableList<String> transitiveClasspath, ImmutableSet<String> directJars, ImmutableList<String> depsArtifacts)190   public static Collection<String> reduceClasspath(
191       ImmutableList<String> transitiveClasspath,
192       ImmutableSet<String> directJars,
193       ImmutableList<String> depsArtifacts) {
194     if (directJars.isEmpty()) {
195       // the compilation doesn't support strict deps (e.g. proto libraries)
196       // TODO(cushon): make this a usage error
197       return transitiveClasspath;
198     }
199     Set<String> reduced = new HashSet<>(directJars);
200     for (String path : depsArtifacts) {
201       DepsProto.Dependencies.Builder deps = DepsProto.Dependencies.newBuilder();
202       try (InputStream is = new BufferedInputStream(Files.newInputStream(Paths.get(path)))) {
203         deps.mergeFrom(is);
204       } catch (IOException e) {
205         throw new IOError(e);
206       }
207       for (DepsProto.Dependency dep : deps.build().getDependencyList()) {
208         switch (dep.getKind()) {
209           case EXPLICIT:
210           case IMPLICIT:
211             reduced.add(dep.getPath());
212             break;
213           case INCOMPLETE:
214           case UNUSED:
215             break;
216         }
217       }
218     }
219     // preserve the order of entries in the transitive classpath
220     return Collections2.filter(transitiveClasspath, Predicates.in(reduced));
221   }
222 }
223