Path: blob/master/src/jdk.compiler/share/classes/com/sun/tools/sjavac/Package.java
41175 views
/*1* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.tools.sjavac;2627import java.io.File;28import java.net.URI;29import java.util.ArrayList;30import java.util.Collections;31import java.util.HashMap;32import java.util.HashSet;33import java.util.List;34import java.util.Map;35import java.util.Set;36import java.util.TreeMap;37import java.util.regex.Matcher;38import java.util.regex.Pattern;39import java.util.stream.Stream;4041import com.sun.tools.javac.util.Assert;42import com.sun.tools.sjavac.pubapi.PubApi;4344/**45* The Package class maintains meta information about a package.46* For example its sources, dependents,its pubapi and its artifacts.47*48* It might look odd that we track dependents/pubapi/artifacts on49* a package level, but it makes sense since recompiling a full package50* takes as long as recompiling a single java file in that package,51* if you take into account the startup time of the jvm.52*53* Also the dependency information will be much smaller (good for the javac_state file size)54* and it simplifies tracking artifact generation, you do not always know from which55* source a class file was generated, but you always know which package it belongs to.56*57* It is also educational to see package dependencies triggering recompilation of58* other packages. Even though the recompilation was perhaps not necessary,59* the visible recompilation of the dependent packages indicates how much circular60* dependencies your code has.61*62* <p><b>This is NOT part of any supported API.63* If you write code that depends on this, you do so at your own risk.64* This code and its internal interfaces are subject to change or65* deletion without notice.</b>66*/67public class Package implements Comparable<Package> {68// The module this package belongs to. (There is a legacy module with an empty string name,69// used for all legacy sources.)70private Module mod;71// Name of this package, module:pkg72// ex1 jdk.base:java.lang73// ex2 :java.lang (when in legacy mode)74private String name;75// The directory path to the package. If the package belongs to a module,76// then that module's file system name is part of the path.77private String dirname;78// This package has the following dependents, that depend on this package.79private Set<String> dependents = new HashSet<>();8081// Fully qualified name of class in this package -> fully qualified name of dependency82private Map<String, Set<String>> dependencies = new TreeMap<>();83// Fully qualified name of class in this package -> fully qualified name of dependency on class path84private Map<String, Set<String>> cpDependencies = new TreeMap<>();8586// This is the public api of this package.87private PubApi pubApi = new PubApi();88// Map from source file name to Source info object.89private Map<String,Source> sources = new HashMap<>();90// This package generated these artifacts.91private Map<String,File> artifacts = new HashMap<>();9293public Package(Module m, String n) {94int c = n.indexOf(":");95Assert.check(c != -1);96Assert.check(m.name().equals(m.name()));97name = n;98dirname = n.replace('.', File.separatorChar);99if (m.name().length() > 0) {100// There is a module here, prefix the module dir name to the path.101dirname = m.dirname()+File.separatorChar+dirname;102}103}104105public Module mod() { return mod; }106public String name() { return name; }107public String dirname() { return dirname; }108public Map<String,Source> sources() { return sources; }109public Map<String,File> artifacts() { return artifacts; }110public PubApi getPubApi() { return pubApi; }111112public Map<String,Set<String>> typeDependencies() { return dependencies; }113public Map<String,Set<String>> typeClasspathDependencies() { return cpDependencies; }114115public Set<String> dependents() { return dependents; }116117@Override118public boolean equals(Object o) {119return (o instanceof Package pac) && name.equals(pac.name);120}121122@Override123public int hashCode() {124return name.hashCode();125}126127@Override128public int compareTo(Package o) {129return name.compareTo(o.name);130}131132public void addSource(Source s) {133sources.put(s.file().getPath(), s);134}135136private static Pattern DEP_PATTERN = Pattern.compile("(.*) -> (.*)");137public void parseAndAddDependency(String d, boolean cp) {138Matcher m = DEP_PATTERN.matcher(d);139if (!m.matches())140throw new IllegalArgumentException("Bad dependency string: " + d);141addDependency(m.group(1), m.group(2), cp);142}143144public void addDependency(String fullyQualifiedFrom,145String fullyQualifiedTo,146boolean cp) {147Map<String, Set<String>> map = cp ? cpDependencies : dependencies;148if (!map.containsKey(fullyQualifiedFrom))149map.put(fullyQualifiedFrom, new HashSet<>());150map.get(fullyQualifiedFrom).add(fullyQualifiedTo);151}152153public void addDependent(String d) {154dependents.add(d);155}156157/**158* Check if we have knowledge in the javac state that159* describe the results of compiling this package before.160*/161public boolean existsInJavacState() {162return artifacts.size() > 0 || !pubApi.isEmpty();163}164165public boolean hasPubApiChanged(PubApi newPubApi) {166return !newPubApi.isBackwardCompatibleWith(pubApi);167}168169public void setPubapi(PubApi newPubApi) {170pubApi = newPubApi;171}172173public void setDependencies(Map<String, Set<String>> ds, boolean cp) {174(cp ? cpDependencies : dependencies).clear();175for (String fullyQualifiedFrom : ds.keySet())176for (String fullyQualifiedTo : ds.get(fullyQualifiedFrom))177addDependency(fullyQualifiedFrom, fullyQualifiedTo, cp);178}179180public void save(StringBuilder b) {181b.append("P ").append(name).append("\n");182Source.saveSources(sources, b);183saveDependencies(b);184savePubapi(b);185saveArtifacts(b);186}187188public static Package load(Module module, String l) {189String name = l.substring(2);190return new Package(module, name);191}192193public void saveDependencies(StringBuilder b) {194195// Dependencies where *to* is among sources196for (String fullyQualifiedFrom : dependencies.keySet()) {197for (String fullyQualifiedTo : dependencies.get(fullyQualifiedFrom)) {198b.append(String.format("D S %s -> %s%n", fullyQualifiedFrom, fullyQualifiedTo));199}200}201202// Dependencies where *to* is on class path203for (String fullyQualifiedFrom : cpDependencies.keySet()) {204for (String fullyQualifiedTo : cpDependencies.get(fullyQualifiedFrom)) {205b.append(String.format("D C %s -> %s%n", fullyQualifiedFrom, fullyQualifiedTo));206}207}208}209210public void savePubapi(StringBuilder b) {211pubApi.asListOfStrings()212.stream()213.flatMap(l -> Stream.of("I ", l, "\n"))214.forEach(b::append);215}216217public static void savePackages(Map<String,Package> packages, StringBuilder b) {218List<String> sorted_packages = new ArrayList<>();219for (String key : packages.keySet() ) {220sorted_packages.add(key);221}222Collections.sort(sorted_packages);223for (String s : sorted_packages) {224Package p = packages.get(s);225p.save(b);226}227}228229public void addArtifact(String a) {230artifacts.put(a, new File(a));231}232233public void addArtifact(File f) {234artifacts.put(f.getPath(), f);235}236237public void addArtifacts(Set<URI> as) {238for (URI u : as) {239addArtifact(new File(u));240}241}242243public void setArtifacts(Set<URI> as) {244Assert.check(!artifacts.isEmpty());245artifacts = new HashMap<>();246addArtifacts(as);247}248249public void loadArtifact(String l) {250// Find next space after "A ".251int dp = l.indexOf(' ',2);252String fn = l.substring(2,dp);253long last_modified = Long.parseLong(l.substring(dp+1));254File f = new File(fn);255if (f.exists() && f.lastModified() != last_modified) {256// Hmm, the artifact on disk does not have the same last modified257// timestamp as the information from the build database.258// We no longer trust the artifact on disk. Delete it.259// The smart javac wrapper will then rebuild the artifact.260Log.debug("Removing "+f.getPath()+" since its timestamp does not match javac_state.");261f.delete();262}263artifacts.put(f.getPath(), f);264}265266public void saveArtifacts(StringBuilder b) {267List<File> sorted_artifacts = new ArrayList<>();268for (File f : artifacts.values()) {269sorted_artifacts.add(f);270}271Collections.sort(sorted_artifacts);272for (File f : sorted_artifacts) {273// The last modified information is only used274// to detect tampering with the output dir.275// If the outputdir has been modified, not by javac,276// then a mismatch will be detected in the last modified277// timestamps stored in the build database compared278// to the timestamps on disk and the artifact will be deleted.279280b.append("A "+f.getPath()+" "+f.lastModified()+"\n");281}282}283284/**285* Always clean out a tainted package before it is recompiled.286*/287public void deleteArtifacts() {288for (File a : artifacts.values()) {289a.delete();290}291}292}293294295