mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-24 06:40:05 +00:00
294 lines
11 KiB
Java
294 lines
11 KiB
Java
/*
|
|
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package com.sun.tools.sjavac;
|
|
|
|
import java.io.File;
|
|
import java.net.URI;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.TreeMap;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
import java.util.stream.Stream;
|
|
|
|
import com.sun.tools.javac.util.Assert;
|
|
import com.sun.tools.sjavac.pubapi.PubApi;
|
|
|
|
/**
|
|
* The Package class maintains meta information about a package.
|
|
* For example its sources, dependents,its pubapi and its artifacts.
|
|
*
|
|
* It might look odd that we track dependents/pubapi/artifacts on
|
|
* a package level, but it makes sense since recompiling a full package
|
|
* takes as long as recompiling a single java file in that package,
|
|
* if you take into account the startup time of the jvm.
|
|
*
|
|
* Also the dependency information will be much smaller (good for the javac_state file size)
|
|
* and it simplifies tracking artifact generation, you do not always know from which
|
|
* source a class file was generated, but you always know which package it belongs to.
|
|
*
|
|
* It is also educational to see package dependencies triggering recompilation of
|
|
* other packages. Even though the recompilation was perhaps not necessary,
|
|
* the visible recompilation of the dependent packages indicates how much circular
|
|
* dependencies your code has.
|
|
*
|
|
* <p><b>This is NOT part of any supported API.
|
|
* If you write code that depends on this, you do so at your own risk.
|
|
* This code and its internal interfaces are subject to change or
|
|
* deletion without notice.</b>
|
|
*/
|
|
public class Package implements Comparable<Package> {
|
|
// The module this package belongs to. (There is a legacy module with an empty string name,
|
|
// used for all legacy sources.)
|
|
private Module mod;
|
|
// Name of this package, module:pkg
|
|
// ex1 jdk.base:java.lang
|
|
// ex2 :java.lang (when in legacy mode)
|
|
private String name;
|
|
// The directory path to the package. If the package belongs to a module,
|
|
// then that module's file system name is part of the path.
|
|
private String dirname;
|
|
// This package has the following dependents, that depend on this package.
|
|
private Set<String> dependents = new HashSet<>();
|
|
|
|
// Fully qualified name of class in this package -> fully qualified name of dependency
|
|
private Map<String, Set<String>> dependencies = new TreeMap<>();
|
|
// Fully qualified name of class in this package -> fully qualified name of dependency on class path
|
|
private Map<String, Set<String>> cpDependencies = new TreeMap<>();
|
|
|
|
// This is the public api of this package.
|
|
private PubApi pubApi = new PubApi();
|
|
// Map from source file name to Source info object.
|
|
private Map<String,Source> sources = new HashMap<>();
|
|
// This package generated these artifacts.
|
|
private Map<String,File> artifacts = new HashMap<>();
|
|
|
|
public Package(Module m, String n) {
|
|
int c = n.indexOf(":");
|
|
Assert.check(c != -1);
|
|
Assert.check(m.name().equals(m.name()));
|
|
name = n;
|
|
dirname = n.replace('.', File.separatorChar);
|
|
if (m.name().length() > 0) {
|
|
// There is a module here, prefix the module dir name to the path.
|
|
dirname = m.dirname()+File.separatorChar+dirname;
|
|
}
|
|
}
|
|
|
|
public Module mod() { return mod; }
|
|
public String name() { return name; }
|
|
public String dirname() { return dirname; }
|
|
public Map<String,Source> sources() { return sources; }
|
|
public Map<String,File> artifacts() { return artifacts; }
|
|
public PubApi getPubApi() { return pubApi; }
|
|
|
|
public Map<String,Set<String>> typeDependencies() { return dependencies; }
|
|
public Map<String,Set<String>> typeClasspathDependencies() { return cpDependencies; }
|
|
|
|
public Set<String> dependents() { return dependents; }
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
return (o instanceof Package) && name.equals(((Package)o).name);
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return name.hashCode();
|
|
}
|
|
|
|
@Override
|
|
public int compareTo(Package o) {
|
|
return name.compareTo(o.name);
|
|
}
|
|
|
|
public void addSource(Source s) {
|
|
sources.put(s.file().getPath(), s);
|
|
}
|
|
|
|
private static Pattern DEP_PATTERN = Pattern.compile("(.*) -> (.*)");
|
|
public void parseAndAddDependency(String d, boolean cp) {
|
|
Matcher m = DEP_PATTERN.matcher(d);
|
|
if (!m.matches())
|
|
throw new IllegalArgumentException("Bad dependency string: " + d);
|
|
addDependency(m.group(1), m.group(2), cp);
|
|
}
|
|
|
|
public void addDependency(String fullyQualifiedFrom,
|
|
String fullyQualifiedTo,
|
|
boolean cp) {
|
|
Map<String, Set<String>> map = cp ? cpDependencies : dependencies;
|
|
if (!map.containsKey(fullyQualifiedFrom))
|
|
map.put(fullyQualifiedFrom, new HashSet<>());
|
|
map.get(fullyQualifiedFrom).add(fullyQualifiedTo);
|
|
}
|
|
|
|
public void addDependent(String d) {
|
|
dependents.add(d);
|
|
}
|
|
|
|
/**
|
|
* Check if we have knowledge in the javac state that
|
|
* describe the results of compiling this package before.
|
|
*/
|
|
public boolean existsInJavacState() {
|
|
return artifacts.size() > 0 || !pubApi.isEmpty();
|
|
}
|
|
|
|
public boolean hasPubApiChanged(PubApi newPubApi) {
|
|
return !newPubApi.isBackwardCompatibleWith(pubApi);
|
|
}
|
|
|
|
public void setPubapi(PubApi newPubApi) {
|
|
pubApi = newPubApi;
|
|
}
|
|
|
|
public void setDependencies(Map<String, Set<String>> ds, boolean cp) {
|
|
(cp ? cpDependencies : dependencies).clear();
|
|
for (String fullyQualifiedFrom : ds.keySet())
|
|
for (String fullyQualifiedTo : ds.get(fullyQualifiedFrom))
|
|
addDependency(fullyQualifiedFrom, fullyQualifiedTo, cp);
|
|
}
|
|
|
|
public void save(StringBuilder b) {
|
|
b.append("P ").append(name).append("\n");
|
|
Source.saveSources(sources, b);
|
|
saveDependencies(b);
|
|
savePubapi(b);
|
|
saveArtifacts(b);
|
|
}
|
|
|
|
public static Package load(Module module, String l) {
|
|
String name = l.substring(2);
|
|
return new Package(module, name);
|
|
}
|
|
|
|
public void saveDependencies(StringBuilder b) {
|
|
|
|
// Dependencies where *to* is among sources
|
|
for (String fullyQualifiedFrom : dependencies.keySet()) {
|
|
for (String fullyQualifiedTo : dependencies.get(fullyQualifiedFrom)) {
|
|
b.append(String.format("D S %s -> %s%n", fullyQualifiedFrom, fullyQualifiedTo));
|
|
}
|
|
}
|
|
|
|
// Dependencies where *to* is on class path
|
|
for (String fullyQualifiedFrom : cpDependencies.keySet()) {
|
|
for (String fullyQualifiedTo : cpDependencies.get(fullyQualifiedFrom)) {
|
|
b.append(String.format("D C %s -> %s%n", fullyQualifiedFrom, fullyQualifiedTo));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void savePubapi(StringBuilder b) {
|
|
pubApi.asListOfStrings()
|
|
.stream()
|
|
.flatMap(l -> Stream.of("I ", l, "\n"))
|
|
.forEach(b::append);
|
|
}
|
|
|
|
public static void savePackages(Map<String,Package> packages, StringBuilder b) {
|
|
List<String> sorted_packages = new ArrayList<>();
|
|
for (String key : packages.keySet() ) {
|
|
sorted_packages.add(key);
|
|
}
|
|
Collections.sort(sorted_packages);
|
|
for (String s : sorted_packages) {
|
|
Package p = packages.get(s);
|
|
p.save(b);
|
|
}
|
|
}
|
|
|
|
public void addArtifact(String a) {
|
|
artifacts.put(a, new File(a));
|
|
}
|
|
|
|
public void addArtifact(File f) {
|
|
artifacts.put(f.getPath(), f);
|
|
}
|
|
|
|
public void addArtifacts(Set<URI> as) {
|
|
for (URI u : as) {
|
|
addArtifact(new File(u));
|
|
}
|
|
}
|
|
|
|
public void setArtifacts(Set<URI> as) {
|
|
Assert.check(!artifacts.isEmpty());
|
|
artifacts = new HashMap<>();
|
|
addArtifacts(as);
|
|
}
|
|
|
|
public void loadArtifact(String l) {
|
|
// Find next space after "A ".
|
|
int dp = l.indexOf(' ',2);
|
|
String fn = l.substring(2,dp);
|
|
long last_modified = Long.parseLong(l.substring(dp+1));
|
|
File f = new File(fn);
|
|
if (f.exists() && f.lastModified() != last_modified) {
|
|
// Hmm, the artifact on disk does not have the same last modified
|
|
// timestamp as the information from the build database.
|
|
// We no longer trust the artifact on disk. Delete it.
|
|
// The smart javac wrapper will then rebuild the artifact.
|
|
Log.debug("Removing "+f.getPath()+" since its timestamp does not match javac_state.");
|
|
f.delete();
|
|
}
|
|
artifacts.put(f.getPath(), f);
|
|
}
|
|
|
|
public void saveArtifacts(StringBuilder b) {
|
|
List<File> sorted_artifacts = new ArrayList<>();
|
|
for (File f : artifacts.values()) {
|
|
sorted_artifacts.add(f);
|
|
}
|
|
Collections.sort(sorted_artifacts);
|
|
for (File f : sorted_artifacts) {
|
|
// The last modified information is only used
|
|
// to detect tampering with the output dir.
|
|
// If the outputdir has been modified, not by javac,
|
|
// then a mismatch will be detected in the last modified
|
|
// timestamps stored in the build database compared
|
|
// to the timestamps on disk and the artifact will be deleted.
|
|
|
|
b.append("A "+f.getPath()+" "+f.lastModified()+"\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Always clean out a tainted package before it is recompiled.
|
|
*/
|
|
public void deleteArtifacts() {
|
|
for (File a : artifacts.values()) {
|
|
a.delete();
|
|
}
|
|
}
|
|
}
|