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