8004658: Add internal smart javac wrapper to solve JEP 139
Reviewed-by: jjg
This commit is contained in:
parent
0a95b1d28c
commit
3d5f55b851
@ -29,18 +29,18 @@
|
||||
# Override this path as needed, either on the command line or in
|
||||
# one of the standard user build.properties files (see build.xml)
|
||||
|
||||
# boot.java.home = /opt/jdk/1.6.0
|
||||
# boot.java.home = /opt/jdk/1.7.0
|
||||
boot.java = ${boot.java.home}/bin/java
|
||||
boot.javac = ${boot.java.home}/bin/javac
|
||||
boot.javac.source = 6
|
||||
boot.javac.target = 6
|
||||
boot.javac.source = 7
|
||||
boot.javac.target = 7
|
||||
|
||||
# This is the JDK used to run the product version of the tools,
|
||||
# for example, for testing. If you're building a complete JDK, specify that.
|
||||
# Override this path as needed, either on the command line or in
|
||||
# one of the standard user build.properties files (see build.xml)
|
||||
|
||||
# target.java.home = /opt/jdk/1.7.0
|
||||
# target.java.home = /opt/jdk/1.8.0
|
||||
target.java = ${target.java.home}/bin/java
|
||||
|
||||
# Version info -- override as needed
|
||||
@ -161,6 +161,14 @@ javap.tests = \
|
||||
|
||||
#
|
||||
|
||||
sjavac.includes = \
|
||||
com/sun/tools/sjavac/
|
||||
|
||||
sjavac.tests = \
|
||||
tools/sjavac
|
||||
|
||||
#
|
||||
|
||||
# The following files require the latest JDK to be available.
|
||||
# The API can be provided by using a suitable boot.java.home
|
||||
# or by setting import.jdk
|
||||
|
@ -241,15 +241,15 @@
|
||||
</target>
|
||||
|
||||
<target name="build-bootstrap-tools"
|
||||
depends="build-bootstrap-javac,build-bootstrap-javadoc,build-bootstrap-doclets,build-bootstrap-javah"
|
||||
depends="build-bootstrap-javac,build-bootstrap-javadoc,build-bootstrap-doclets,build-bootstrap-javah,build-bootstrap-sjavac"
|
||||
/>
|
||||
|
||||
<target name="build-all-tools"
|
||||
depends="build-javac,build-javadoc,build-doclets,build-javah,build-javap"
|
||||
depends="build-javac,build-javadoc,build-doclets,build-javah,build-javap,build-sjavac"
|
||||
/>
|
||||
|
||||
<target name="build-all-classes" depends="build-bootstrap-javac,-create-import-jdk-stubs">
|
||||
<build-classes includes="${javac.includes} ${javadoc.includes} ${doclets.includes} ${javah.includes} ${javap.includes}"/>
|
||||
<build-classes includes="${javac.includes} ${javadoc.includes} ${doclets.includes} ${javah.includes} ${javap.includes} ${sjavac.includes}"/>
|
||||
</target>
|
||||
|
||||
<!-- clean -->
|
||||
@ -656,6 +656,40 @@
|
||||
|
||||
<target name="javap" depends="build-javap,jtreg-javap,findbugs-javap"/>
|
||||
|
||||
<!--
|
||||
**** sjavac targets.
|
||||
-->
|
||||
|
||||
<target name="build-bootstrap-sjavac"
|
||||
depends="-def-build-bootstrap-classes,-def-build-bootstrap-jar,-def-build-bootstrap-tool">
|
||||
<build-bootstrap-classes includes="${sjavac.includes}"/>
|
||||
<build-bootstrap-jar name="sjavac" includes="${sjavac.includes}"
|
||||
jarmainclass="com.sun.tools.sjavac.Main"/>
|
||||
<build-bootstrap-tool name="sjavac"/>
|
||||
</target>
|
||||
|
||||
<target name="build-classes-sjavac" depends="build-classes-javac">
|
||||
<build-classes includes="${sjavac.includes}"/>
|
||||
</target>
|
||||
|
||||
<target name="build-sjavac" depends="build-classes-sjavac">
|
||||
<build-jar name="sjavac" includes="${sjavac.includes}"
|
||||
jarmainclass="com.sun.tools.sjavac.Main"
|
||||
jarclasspath="sjavac.jar"/>
|
||||
<build-tool name="sjavac"/>
|
||||
</target>
|
||||
|
||||
<!-- (no javadoc for javap) -->
|
||||
|
||||
<target name="jtreg-sjavac" depends="build-sjavac,-def-jtreg">
|
||||
<jtreg-tool name="sjavac" tests="${sjavac.tests}"/>
|
||||
</target>
|
||||
|
||||
<target name="findbugs-sjavac" depends="build-sjavac,-def-findbugs">
|
||||
<findbugs-tool name="sjavac"/>
|
||||
</target>
|
||||
|
||||
<target name="sjavac" depends="build-sjavac,jtreg-sjavac,findbugs-sjavac"/>
|
||||
|
||||
<!--
|
||||
**** Create import JDK stubs.
|
||||
|
275
langtools/src/share/classes/com/sun/tools/sjavac/BuildState.java
Normal file
275
langtools/src/share/classes/com/sun/tools/sjavac/BuildState.java
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The build state class captures the source code and generated artifacts
|
||||
* from a build. There are usually two build states, the previous one (prev),
|
||||
* loaded from the javac_state file, and the current one (now).
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class BuildState {
|
||||
private Map<String,Module> modules = new HashMap<String,Module>();
|
||||
private Map<String,Package> packages = new HashMap<String,Package>();
|
||||
private Map<String,Source> sources = new HashMap<String,Source>();
|
||||
private Map<String,File> artifacts = new HashMap<String,File>();
|
||||
// Map from package to a set of packages that depend on said package.
|
||||
private Map<String,Set<String>> dependents = new HashMap<String,Set<String>>();
|
||||
|
||||
public Map<String,Module> modules() { return modules; }
|
||||
public Map<String,Package> packages() { return packages; }
|
||||
public Map<String,Source> sources() { return sources; }
|
||||
public Map<String,File> artifacts() { return artifacts; }
|
||||
public Map<String,Set<String>> dependents() { return dependents; }
|
||||
|
||||
/**
|
||||
* Lookup a module from a name. Create the module if it does
|
||||
* not exist yet.
|
||||
*/
|
||||
public Module lookupModule(String mod) {
|
||||
Module m = modules.get(mod);
|
||||
if (m == null) {
|
||||
m = new Module(mod, "???");
|
||||
modules.put(mod, m);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a module from a given package name. For example:
|
||||
* The package name "base:java.lang" will fetch the module named "base".
|
||||
* The package name ":java.net" will fetch the default module.
|
||||
*/
|
||||
Module findModuleFromPackageName(String pkg) {
|
||||
int cp = pkg.indexOf(':');
|
||||
assert(cp != -1);
|
||||
String mod = pkg.substring(0, cp);
|
||||
return lookupModule(mod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all packages, sources and artifacts for all modules
|
||||
* into the build state.
|
||||
*
|
||||
* @param m The set of modules.
|
||||
*/
|
||||
public void collectPackagesSourcesAndArtifacts(Map<String,Module> m) {
|
||||
modules = m;
|
||||
// Extract all the found packages.
|
||||
for (Module i : modules.values()) {
|
||||
for (Map.Entry<String,Package> j : i.packages().entrySet()) {
|
||||
Package p = packages.get(j.getKey());
|
||||
// Check that no two different packages are stored under same name.
|
||||
assert(p == null || p == j.getValue());
|
||||
if (p == null) {
|
||||
p = j.getValue();
|
||||
packages.put(j.getKey(),j.getValue());
|
||||
}
|
||||
for (Map.Entry<String,Source> k : p.sources().entrySet()) {
|
||||
Source s = sources.get(k.getKey());
|
||||
// Check that no two different sources are stored under same name.
|
||||
assert(s == null || s == k.getValue());
|
||||
if (s == null) {
|
||||
s = k.getValue();
|
||||
sources.put(k.getKey(), k.getValue());
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String,File> g : p.artifacts().entrySet()) {
|
||||
File f = artifacts.get(g.getKey());
|
||||
// Check that no two artifacts are stored under the same file.
|
||||
assert(f == null || f == g.getValue());
|
||||
if (f == null) {
|
||||
f = g.getValue();
|
||||
artifacts.put(g.getKey(), g.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all the artifacts of all modules and packages.
|
||||
*
|
||||
* @param m The set of modules.
|
||||
*/
|
||||
public void collectArtifacts(Map<String,Module> m) {
|
||||
modules = m;
|
||||
// Extract all the found packages.
|
||||
for (Module i : modules.values()) {
|
||||
for (Map.Entry<String,Package> j : i.packages().entrySet()) {
|
||||
Package p = packages.get(j.getKey());
|
||||
// Check that no two different packages are stored under same name.
|
||||
assert(p == null || p == j.getValue());
|
||||
p = j.getValue();
|
||||
packages.put(j.getKey(),j.getValue());
|
||||
for (Map.Entry<String,File> g : p.artifacts().entrySet()) {
|
||||
File f = artifacts.get(g.getKey());
|
||||
// Check that no two artifacts are stored under the same file.
|
||||
assert(f == null || f == g.getValue());
|
||||
artifacts.put(g.getKey(), g.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the package dependents (ie the reverse of the dependencies).
|
||||
*/
|
||||
public void calculateDependents() {
|
||||
dependents = new HashMap<String,Set<String>>();
|
||||
for (String s : packages.keySet()) {
|
||||
Package p = packages.get(s);
|
||||
for (String d : p.dependencies()) {
|
||||
Set<String> ss = dependents.get(d);
|
||||
if (ss == null) {
|
||||
ss = new HashSet<String>();
|
||||
dependents.put(d, ss);
|
||||
}
|
||||
// Add the dependent information to the global dependent map.
|
||||
ss.add(s);
|
||||
Package dp = packages.get(d);
|
||||
// Also add the dependent information to the package specific map.
|
||||
// Normally, you do not compile java.lang et al. Therefore
|
||||
// there are several packages that p depends upon that you
|
||||
// do not have in your state database. This is perfectly fine.
|
||||
if (dp != null) {
|
||||
// But this package did exist in the state database.
|
||||
dp.addDependent(p.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the setModules method above did the right thing when
|
||||
* running through the module->package->source structure.
|
||||
*/
|
||||
public void checkInternalState(String msg, boolean linkedOnly, Map<String,Source> srcs) {
|
||||
boolean baad = false;
|
||||
Map<String,Source> original = new HashMap<String,Source>();
|
||||
Map<String,Source> calculated = new HashMap<String,Source>();
|
||||
|
||||
for (String s : sources.keySet()) {
|
||||
Source ss = sources.get(s);
|
||||
if (ss.isLinkedOnly() == linkedOnly) {
|
||||
calculated.put(s,ss);
|
||||
}
|
||||
}
|
||||
for (String s : srcs.keySet()) {
|
||||
Source ss = srcs.get(s);
|
||||
if (ss.isLinkedOnly() == linkedOnly) {
|
||||
original.put(s,ss);
|
||||
}
|
||||
}
|
||||
if (original.size() != calculated.size()) {
|
||||
Log.error("INTERNAL ERROR "+msg+" original and calculated are not the same size!");
|
||||
baad = true;
|
||||
}
|
||||
if (!original.keySet().equals(calculated.keySet())) {
|
||||
Log.error("INTERNAL ERROR "+msg+" original and calculated do not have the same domain!");
|
||||
baad = true;
|
||||
}
|
||||
if (!baad) {
|
||||
for (String s : original.keySet()) {
|
||||
Source s1 = original.get(s);
|
||||
Source s2 = calculated.get(s);
|
||||
if (s1 == null || s2 == null || !s1.equals(s2)) {
|
||||
Log.error("INTERNAL ERROR "+msg+" original and calculated have differing elements for "+s);
|
||||
}
|
||||
baad = true;
|
||||
}
|
||||
}
|
||||
if (baad) {
|
||||
for (String s : original.keySet()) {
|
||||
Source ss = original.get(s);
|
||||
Source sss = calculated.get(s);
|
||||
if (sss == null) {
|
||||
Log.error("The file "+s+" does not exist in calculated tree of sources.");
|
||||
}
|
||||
}
|
||||
for (String s : calculated.keySet()) {
|
||||
Source ss = calculated.get(s);
|
||||
Source sss = original.get(s);
|
||||
if (sss == null) {
|
||||
Log.error("The file "+s+" does not exist in original set of found sources.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a module from the javac state file.
|
||||
*/
|
||||
public Module loadModule(String l) {
|
||||
Module m = Module.load(l);
|
||||
modules.put(m.name(), m);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a package from the javac state file.
|
||||
*/
|
||||
public Package loadPackage(Module lastModule, String l) {
|
||||
Package p = Package.load(lastModule, l);
|
||||
lastModule.addPackage(p);
|
||||
packages.put(p.name(), p);
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a source from the javac state file.
|
||||
*/
|
||||
public Source loadSource(Package lastPackage, String l, boolean is_generated) {
|
||||
Source s = Source.load(lastPackage, l, is_generated);
|
||||
lastPackage.addSource(s);
|
||||
sources.put(s.name(), s);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* During an incremental compile we need to copy the old javac state
|
||||
* information about packages that were not recompiled.
|
||||
*/
|
||||
public void copyPackagesExcept(BuildState prev, Set<String> recompiled, Set<String> removed) {
|
||||
for (String pkg : prev.packages().keySet()) {
|
||||
// Do not copy recompiled or removed packages.
|
||||
if (recompiled.contains(pkg) || removed.contains(pkg)) continue;
|
||||
Module mnew = findModuleFromPackageName(pkg);
|
||||
Package pprev = prev.packages().get(pkg);
|
||||
mnew.addPackage(pprev);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2012, 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.*;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* The clean properties transform should not be necessary.
|
||||
* Eventually we will cleanup the property file sources in the OpenJDK instead.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class CleanProperties implements Transformer
|
||||
{
|
||||
public void setExtra(String e) {
|
||||
// Any extra information is ignored for clean properties.
|
||||
}
|
||||
|
||||
public void setExtra(String[] a) {
|
||||
// Any extra information is ignored for clean properties.
|
||||
}
|
||||
|
||||
public boolean transform(Map<String,Set<URI>> pkgSrcs,
|
||||
Set<URI> visibleSrcs,
|
||||
Map<URI,Set<String>> visibleClasses,
|
||||
Map<String,Set<String>> oldPackageDependencies,
|
||||
URI destRoot,
|
||||
Map<String,Set<URI>> packageArtifacts,
|
||||
Map<String,Set<String>> packageDependencies,
|
||||
Map<String,String> packagePublicApis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
PrintStream out,
|
||||
PrintStream err)
|
||||
{
|
||||
boolean rc = true;
|
||||
for (String pkgName : pkgSrcs.keySet()) {
|
||||
String pkgNameF = pkgName.replace('.',File.separatorChar);
|
||||
for (URI u : pkgSrcs.get(pkgName)) {
|
||||
File src = new File(u);
|
||||
boolean r = clean(pkgName, pkgNameF, src, new File(destRoot), debugLevel,
|
||||
packageArtifacts);
|
||||
if (r == false) {
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
boolean clean(String pkgName, String pkgNameF, File src, File destRoot, int debugLevel,
|
||||
Map<String,Set<URI>> packageArtifacts)
|
||||
{
|
||||
// Load the properties file.
|
||||
Properties p = new Properties();
|
||||
try {
|
||||
p.load(new FileInputStream(src));
|
||||
} catch (IOException e) {
|
||||
Log.error("Error reading file "+src.getPath());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort the properties in increasing key order.
|
||||
List<String> sortedKeys = new ArrayList<String>();
|
||||
for (Object key : p.keySet()) {
|
||||
sortedKeys.add((String)key);
|
||||
}
|
||||
Collections.sort(sortedKeys);
|
||||
Iterator<String> keys = sortedKeys.iterator();
|
||||
|
||||
// Collect the properties into a string buffer.
|
||||
StringBuilder data = new StringBuilder();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
data.append(CompileProperties.escape(key)+":"+CompileProperties.escape((String)p.get(key))+"\n");
|
||||
}
|
||||
|
||||
String destFilename = destRoot.getPath()+File.separator+pkgNameF+File.separator+src.getName();
|
||||
File dest = new File(destFilename);
|
||||
|
||||
// Make sure the dest directories exist.
|
||||
if (!dest.getParentFile().isDirectory()) {
|
||||
if (!dest.getParentFile().mkdirs()) {
|
||||
Log.error("Could not create the directory "+dest.getParentFile().getPath());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Set<URI> as = packageArtifacts.get(pkgName);
|
||||
if (as == null) {
|
||||
as = new HashSet<URI>();
|
||||
packageArtifacts.put(pkgName, as);
|
||||
}
|
||||
as.add(dest.toURI());
|
||||
|
||||
if (dest.exists() && dest.lastModified() > src.lastModified()) {
|
||||
// A cleaned property file exists, and its timestamp is newer than the source.
|
||||
// Assume that we do not need to clean!
|
||||
// Thus we are done.
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.info("Cleaning property file "+pkgNameF+File.separator+src.getName());
|
||||
try (Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dest)))) {
|
||||
writer.write(data.toString());
|
||||
} catch ( IOException e ) {
|
||||
Log.error("Could not write file "+dest.getPath());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.net.URI;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A compile chunk is a list of sources/packages to be compiled. Possibly a subset of
|
||||
* the total number of sources/packages to be compiled for this sjavac invocation.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class CompileChunk implements Comparable<CompileChunk> {
|
||||
public int numPackages;
|
||||
public int numDependents;
|
||||
public Set<URI> srcs = new HashSet<URI>();
|
||||
public StringBuilder pkgNames = new StringBuilder();
|
||||
public String pkgFromTos = "";
|
||||
|
||||
public int compareTo(CompileChunk c) {
|
||||
if (numDependents == c.numDependents) return 0;
|
||||
if (numDependents > c.numDependents) return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean equal(CompileChunk c) {
|
||||
return numDependents == c.numDependents;
|
||||
}
|
||||
}
|
@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sun.tools.sjavac.server.JavacServer;
|
||||
import com.sun.tools.sjavac.server.SysInfo;
|
||||
import java.io.PrintStream;
|
||||
|
||||
/**
|
||||
* This transform compiles a set of packages containing Java sources.
|
||||
* The compile request is divided into separate sets of source files.
|
||||
* For each set a separate request thread is dispatched to a javac server
|
||||
* and the meta data is accumulated. The number of sets correspond more or
|
||||
* less to the number of cores. Less so now, than it will in the future.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class CompileJavaPackages implements Transformer {
|
||||
|
||||
// The current limited sharing of data between concurrent JavaCompilers
|
||||
// in the server will not give speedups above 3 cores. Thus this limit.
|
||||
// We hope to improve this in the future.
|
||||
final static int limitOnConcurrency = 3;
|
||||
|
||||
String serverSettings;
|
||||
public void setExtra(String e) {
|
||||
serverSettings = e;
|
||||
}
|
||||
|
||||
String[] args;
|
||||
public void setExtra(String[] a) {
|
||||
args = a;
|
||||
}
|
||||
|
||||
public boolean transform(Map<String,Set<URI>> pkgSrcs,
|
||||
Set<URI> visibleSources,
|
||||
Map<URI,Set<String>> visibleClasses,
|
||||
Map<String,Set<String>> oldPackageDependents,
|
||||
URI destRoot,
|
||||
final Map<String,Set<URI>> packageArtifacts,
|
||||
final Map<String,Set<String>> packageDependencies,
|
||||
final Map<String,String> packagePubapis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
PrintStream out,
|
||||
PrintStream err)
|
||||
{
|
||||
boolean rc = true;
|
||||
boolean concurrentCompiles = true;
|
||||
|
||||
// Fetch the id.
|
||||
String id = Util.extractStringOption("id", serverSettings);
|
||||
if (id == null || id.equals("")) {
|
||||
// No explicit id set. Create a random id so that the requests can be
|
||||
// grouped properly in the server.
|
||||
id = "id"+(((new Random()).nextLong())&Long.MAX_VALUE);
|
||||
}
|
||||
// Only keep portfile and sjavac settings..
|
||||
String psServerSettings = Util.cleanSubOptions("--server:", Util.set("portfile","sjavac","background","keepalive"), serverSettings);
|
||||
|
||||
// Get maximum heap size from the server!
|
||||
SysInfo sysinfo = JavacServer.connectGetSysInfo(psServerSettings, out, err);
|
||||
if (sysinfo.numCores == -1) {
|
||||
Log.error("Could not query server for sysinfo!");
|
||||
return false;
|
||||
}
|
||||
int numMBytes = (int)(sysinfo.maxMemory / ((long)(1024*1024)));
|
||||
Log.debug("Server reports "+numMBytes+"MiB of memory and "+sysinfo.numCores+" cores");
|
||||
|
||||
if (numCores <= 0) {
|
||||
// Set the requested number of cores to the number of cores on the server.
|
||||
numCores = sysinfo.numCores;
|
||||
Log.debug("Number of jobs not explicitly set, defaulting to "+sysinfo.numCores);
|
||||
} else if (sysinfo.numCores < numCores) {
|
||||
// Set the requested number of cores to the number of cores on the server.
|
||||
Log.debug("Limiting jobs from explicitly set "+numCores+" to cores available on server: "+sysinfo.numCores);
|
||||
numCores = sysinfo.numCores;
|
||||
} else {
|
||||
Log.debug("Number of jobs explicitly set to "+numCores);
|
||||
}
|
||||
// More than three concurrent cores does not currently give a speedup, at least for compiling the jdk
|
||||
// in the OpenJDK. This will change in the future.
|
||||
int numCompiles = numCores;
|
||||
if (numCores > limitOnConcurrency) numCompiles = limitOnConcurrency;
|
||||
// Split the work up in chunks to compiled.
|
||||
|
||||
int numSources = 0;
|
||||
for (String s : pkgSrcs.keySet()) {
|
||||
Set<URI> ss = pkgSrcs.get(s);
|
||||
numSources += ss.size();
|
||||
}
|
||||
|
||||
int sourcesPerCompile = numSources / numCompiles;
|
||||
|
||||
// For 64 bit Java, it seems we can compile the OpenJDK 8800 files with a 1500M of heap
|
||||
// in a single chunk, with reasonable performance.
|
||||
// For 32 bit java, it seems we need 1G of heap.
|
||||
// Number experimentally determined when compiling the OpenJDK.
|
||||
// Includes space for reasonably efficient garbage collection etc,
|
||||
// Calculating backwards gives us a requirement of
|
||||
// 1500M/8800 = 175 KiB for 64 bit platforms
|
||||
// and 1G/8800 = 119 KiB for 32 bit platform
|
||||
// for each compile.....
|
||||
int kbPerFile = 175;
|
||||
String osarch = System.getProperty("os.arch");
|
||||
if (osarch.equals("i386")) {
|
||||
// For 32 bit platforms, assume it is slightly smaller
|
||||
// because of smaller object headers and pointers.
|
||||
kbPerFile = 119;
|
||||
}
|
||||
int numRequiredMBytes = (kbPerFile*numSources)/1024;
|
||||
Log.debug("For os.arch "+osarch+" the empirically determined heap required per file is "+kbPerFile+"KiB");
|
||||
Log.debug("Server has "+numMBytes+"MiB of heap.");
|
||||
Log.debug("Heuristics say that we need "+numRequiredMBytes+"MiB of heap for all source files.");
|
||||
// Perform heuristics to see how many cores we can use,
|
||||
// or if we have to the work serially in smaller chunks.
|
||||
if (numMBytes < numRequiredMBytes) {
|
||||
// Ouch, cannot fit even a single compile into the heap.
|
||||
// Split it up into several serial chunks.
|
||||
concurrentCompiles = false;
|
||||
// Limit the number of sources for each compile to 500.
|
||||
if (numSources < 500) {
|
||||
numCompiles = 1;
|
||||
sourcesPerCompile = numSources;
|
||||
Log.debug("Compiling as a single source code chunk to stay within heap size limitations!");
|
||||
} else if (sourcesPerCompile > 500) {
|
||||
// This number is very low, and tuned to dealing with the OpenJDK
|
||||
// where the source is >very< circular! In normal application,
|
||||
// with less circularity the number could perhaps be increased.
|
||||
numCompiles = numSources / 500;
|
||||
sourcesPerCompile = numSources/numCompiles;
|
||||
Log.debug("Compiling source as "+numCompiles+" code chunks serially to stay within heap size limitations!");
|
||||
}
|
||||
} else {
|
||||
if (numCompiles > 1) {
|
||||
// Ok, we can fit at least one full compilation on the heap.
|
||||
float usagePerCompile = (float)numRequiredMBytes / ((float)numCompiles * (float)0.7);
|
||||
int usage = (int)(usagePerCompile * (float)numCompiles);
|
||||
Log.debug("Heuristics say that for "+numCompiles+" concurrent compiles we need "+usage+"MiB");
|
||||
if (usage > numMBytes) {
|
||||
// Ouch it does not fit. Reduce to a single chunk.
|
||||
numCompiles = 1;
|
||||
sourcesPerCompile = numSources;
|
||||
// What if the relationship betweem number of compile_chunks and num_required_mbytes
|
||||
// is not linear? Then perhaps 2 chunks would fit where 3 does not. Well, this is
|
||||
// something to experiment upon in the future.
|
||||
Log.debug("Limiting compile to a single thread to stay within heap size limitations!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.debug("Compiling sources in "+numCompiles+" chunk(s)");
|
||||
|
||||
// Create the chunks to be compiled.
|
||||
final CompileChunk[] compileChunks = createCompileChunks(pkgSrcs, oldPackageDependents,
|
||||
numCompiles, sourcesPerCompile);
|
||||
|
||||
if (Log.isDebugging()) {
|
||||
int cn = 1;
|
||||
for (CompileChunk cc : compileChunks) {
|
||||
Log.debug("Chunk "+cn+" for "+id+" ---------------");
|
||||
cn++;
|
||||
for (URI u : cc.srcs) {
|
||||
Log.debug(""+u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The return values for each chunked compile.
|
||||
final int[] rn = new int[numCompiles];
|
||||
// The requets, might or might not run as a background thread.
|
||||
final Thread[] requests = new Thread[numCompiles];
|
||||
|
||||
final Set<URI> fvisible_sources = visibleSources;
|
||||
final Map<URI,Set<String>> fvisible_classes = visibleClasses;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
for (int i=0; i<numCompiles; ++i) {
|
||||
final int ii = i;
|
||||
final CompileChunk cc = compileChunks[i];
|
||||
|
||||
// Pass the num_cores and the id (appended with the chunk number) to the server.
|
||||
final String cleanedServerSettings = psServerSettings+",poolsize="+numCores+",id="+id+"-"+ii;
|
||||
final PrintStream fout = out;
|
||||
final PrintStream ferr = err;
|
||||
|
||||
requests[ii] = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
rn[ii] = JavacServer.useServer(cleanedServerSettings,
|
||||
Main.removeWrapperArgs(args),
|
||||
cc.srcs,
|
||||
fvisible_sources,
|
||||
fvisible_classes,
|
||||
packageArtifacts,
|
||||
packageDependencies,
|
||||
packagePubapis,
|
||||
null,
|
||||
fout, ferr);
|
||||
}
|
||||
};
|
||||
|
||||
if (cc.srcs.size() > 0) {
|
||||
String numdeps = "";
|
||||
if (cc.numDependents > 0) numdeps = "(with "+cc.numDependents+" dependents) ";
|
||||
if (!incremental || cc.numPackages > 16) {
|
||||
String info = "("+cc.pkgFromTos+")";
|
||||
if (info.equals("( to )")) {
|
||||
info = "";
|
||||
}
|
||||
Log.info("Compiling "+cc.srcs.size()+" files "+numdeps+"in "+cc.numPackages+" packages "+info);
|
||||
} else {
|
||||
Log.info("Compiling "+cc.pkgNames+numdeps);
|
||||
}
|
||||
if (concurrentCompiles) {
|
||||
requests[ii].start();
|
||||
}
|
||||
else {
|
||||
requests[ii].run();
|
||||
// If there was an error, then stop early when running single threaded.
|
||||
if (rn[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (concurrentCompiles) {
|
||||
// If there are background threads for the concurrent compiles, then join them.
|
||||
for (int i=0; i<numCompiles; ++i) {
|
||||
try { requests[i].join(); } catch (InterruptedException e) { }
|
||||
}
|
||||
}
|
||||
|
||||
// Check the return values.
|
||||
for (int i=0; i<numCompiles; ++i) {
|
||||
if (compileChunks[i].srcs.size() > 0) {
|
||||
if (rn[i] != 0) {
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
long minutes = duration/60000;
|
||||
long seconds = (duration-minutes*60000)/1000;
|
||||
Log.debug("Compilation of "+numSources+" source files took "+minutes+"m "+seconds+"s");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Split up the sources into compile chunks. If old package dependents information
|
||||
* is available, sort the order of the chunks into the most dependent first!
|
||||
* (Typically that chunk contains the java.lang package.) In the future
|
||||
* we could perhaps improve the heuristics to put the sources into even more sensible chunks.
|
||||
* Now the package are simple sorted in alphabetical order and chunked, then the chunks
|
||||
* are sorted on how dependent they are.
|
||||
*
|
||||
* @param pkgSrcs The sources to compile.
|
||||
* @param oldPackageDependents Old package dependents, if non-empty, used to sort the chunks.
|
||||
* @param numCompiles The number of chunks.
|
||||
* @param sourcesPerCompile The number of sources per chunk.
|
||||
* @return
|
||||
*/
|
||||
CompileChunk[] createCompileChunks(Map<String,Set<URI>> pkgSrcs,
|
||||
Map<String,Set<String>> oldPackageDependents,
|
||||
int numCompiles,
|
||||
int sourcesPerCompile) {
|
||||
|
||||
CompileChunk[] compileChunks = new CompileChunk[numCompiles];
|
||||
for (int i=0; i<compileChunks.length; ++i) {
|
||||
compileChunks[i] = new CompileChunk();
|
||||
}
|
||||
|
||||
// Now go through the packages and spread out the source on the different chunks.
|
||||
int ci = 0;
|
||||
// Sort the packages
|
||||
String[] packageNames = pkgSrcs.keySet().toArray(new String[0]);
|
||||
Arrays.sort(packageNames);
|
||||
String from = null;
|
||||
for (String pkgName : packageNames) {
|
||||
CompileChunk cc = compileChunks[ci];
|
||||
Set<URI> s = pkgSrcs.get(pkgName);
|
||||
if (cc.srcs.size()+s.size() > sourcesPerCompile && ci < numCompiles-1) {
|
||||
from = null;
|
||||
ci++;
|
||||
cc = compileChunks[ci];
|
||||
}
|
||||
cc.numPackages++;
|
||||
cc.srcs.addAll(s);
|
||||
|
||||
// Calculate nice package names to use as information when compiling.
|
||||
String justPkgName = Util.justPackageName(pkgName);
|
||||
// Fetch how many packages depend on this package from the old build state.
|
||||
Set<String> ss = oldPackageDependents.get(pkgName);
|
||||
if (ss != null) {
|
||||
// Accumulate this information onto this chunk.
|
||||
cc.numDependents += ss.size();
|
||||
}
|
||||
if (from == null || from.trim().equals("")) from = justPkgName;
|
||||
cc.pkgNames.append(justPkgName+"("+s.size()+") ");
|
||||
cc.pkgFromTos = from+" to "+justPkgName;
|
||||
}
|
||||
// If we are compiling serially, sort the chunks, so that the chunk (with the most dependents) (usually the chunk
|
||||
// containing java.lang.Object, is to be compiled first!
|
||||
// For concurrent compilation, this does not matter.
|
||||
Arrays.sort(compileChunks);
|
||||
return compileChunks;
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.*;
|
||||
import java.net.URI;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Compile properties transform a properties file into a Java source file.
|
||||
* Java has built in support for reading properties from either a text file
|
||||
* in the source or a compiled java source file.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class CompileProperties implements Transformer
|
||||
{
|
||||
// Any extra information passed from the command line, for example if:
|
||||
// -tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle
|
||||
// then extra will be "sun.util.resources.LocaleNamesBundle"
|
||||
String extra;
|
||||
|
||||
public void setExtra(String e) {
|
||||
extra = e;
|
||||
}
|
||||
|
||||
public void setExtra(String[] a) {
|
||||
}
|
||||
|
||||
public boolean transform(Map<String,Set<URI>> pkgSrcs,
|
||||
Set<URI> visibleSrcs,
|
||||
Map<URI,Set<String>> visibleClasses,
|
||||
Map<String,Set<String>> oldPackageDependents,
|
||||
URI destRoot,
|
||||
Map<String,Set<URI>> packageArtifacts,
|
||||
Map<String,Set<String>> packageDependencies,
|
||||
Map<String,String> packagePublicApis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
PrintStream out,
|
||||
PrintStream err) {
|
||||
boolean rc = true;
|
||||
for (String pkgName : pkgSrcs.keySet()) {
|
||||
String pkgNameF = Util.toFileSystemPath(pkgName);
|
||||
for (URI u : pkgSrcs.get(pkgName)) {
|
||||
File src = new File(u);
|
||||
boolean r = compile(pkgName, pkgNameF, src, new File(destRoot), debugLevel,
|
||||
packageArtifacts);
|
||||
if (r == false) {
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
boolean compile(String pkgName, String pkgNameF, File src, File destRoot, int debugLevel,
|
||||
Map<String,Set<URI>> packageArtifacts)
|
||||
{
|
||||
String superClass = "java.util.ListResourceBundle";
|
||||
|
||||
if (extra != null) {
|
||||
superClass = extra;
|
||||
}
|
||||
// Load the properties file.
|
||||
Properties p = new Properties();
|
||||
try {
|
||||
p.load(new FileInputStream(src));
|
||||
} catch (IOException e) {
|
||||
Log.error("Error reading file "+src.getPath());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate the name of the Java source file to be generated.
|
||||
int dp = src.getName().lastIndexOf(".");
|
||||
String classname = src.getName().substring(0,dp);
|
||||
|
||||
// Sort the properties in increasing key order.
|
||||
List<String> sortedKeys = new ArrayList<String>();
|
||||
for (Object key : p.keySet()) {
|
||||
sortedKeys.add((String)key);
|
||||
}
|
||||
Collections.sort(sortedKeys);
|
||||
Iterator<String> keys = sortedKeys.iterator();
|
||||
|
||||
// Collect the properties into a string buffer.
|
||||
StringBuilder data = new StringBuilder();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
data.append(" { \"" + escape(key) + "\", \"" +
|
||||
escape((String)p.get(key)) + "\" },\n");
|
||||
}
|
||||
|
||||
// Create dest file name. It is derived from the properties file name.
|
||||
String destFilename = destRoot.getPath()+File.separator+pkgNameF+File.separator+classname+".java";
|
||||
File dest = new File(destFilename);
|
||||
|
||||
// Make sure the dest directories exist.
|
||||
if (!dest.getParentFile().isDirectory()) {
|
||||
if (!dest.getParentFile().mkdirs()) {
|
||||
Log.error("Could not create the directory "+dest.getParentFile().getPath());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Set<URI> as = packageArtifacts.get(pkgName);
|
||||
if (as == null) {
|
||||
as = new HashSet<URI>();
|
||||
packageArtifacts.put(pkgName, as);
|
||||
}
|
||||
as.add(dest.toURI());
|
||||
|
||||
if (dest.exists() && dest.lastModified() > src.lastModified()) {
|
||||
// A generated file exists, and its timestamp is newer than the source.
|
||||
// Assume that we do not need to regenerate the dest file!
|
||||
// Thus we are done.
|
||||
return true;
|
||||
}
|
||||
|
||||
String packageString = "package " + pkgNameF.replace(File.separatorChar,'.') + ";\n\n";
|
||||
|
||||
Log.info("Compiling property file "+pkgNameF+File.separator+src.getName());
|
||||
try (Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dest)))) {
|
||||
MessageFormat format = new MessageFormat(FORMAT);
|
||||
writer.write(format.format(new Object[] { packageString, classname, superClass, data }));
|
||||
} catch ( IOException e ) {
|
||||
Log.error("Could not write file "+dest.getPath());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static final String FORMAT =
|
||||
"{0}" +
|
||||
"public final class {1} extends {2} '{'\n" +
|
||||
" protected final Object[][] getContents() '{'\n" +
|
||||
" return new Object[][] '{'\n" +
|
||||
"{3}" +
|
||||
" };\n" +
|
||||
" }\n" +
|
||||
"}\n";
|
||||
|
||||
public static String escape(String theString) {
|
||||
int len = theString.length();
|
||||
StringBuilder outBuffer = new StringBuilder(len*2);
|
||||
|
||||
for(int x=0; x<len; x++) {
|
||||
char aChar = theString.charAt(x);
|
||||
switch(aChar) {
|
||||
case '\\':outBuffer.append('\\'); outBuffer.append('\\');
|
||||
break;
|
||||
case '\t':outBuffer.append('\\'); outBuffer.append('t');
|
||||
break;
|
||||
case '\n':outBuffer.append('\\'); outBuffer.append('n');
|
||||
break;
|
||||
case '\r':outBuffer.append('\\'); outBuffer.append('r');
|
||||
break;
|
||||
case '\f':outBuffer.append('\\'); outBuffer.append('f');
|
||||
break;
|
||||
default:
|
||||
if ((aChar < 0x0020) || (aChar > 0x007e)) {
|
||||
outBuffer.append('\\');
|
||||
outBuffer.append('u');
|
||||
outBuffer.append(toHex((aChar >> 12) & 0xF));
|
||||
outBuffer.append(toHex((aChar >> 8) & 0xF));
|
||||
outBuffer.append(toHex((aChar >> 4) & 0xF));
|
||||
outBuffer.append(toHex( aChar & 0xF));
|
||||
} else {
|
||||
if (aChar == '"') {
|
||||
outBuffer.append('\\');
|
||||
}
|
||||
outBuffer.append(aChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
return outBuffer.toString();
|
||||
}
|
||||
|
||||
private static char toHex(int nibble) {
|
||||
return hexDigit[(nibble & 0xF)];
|
||||
}
|
||||
|
||||
private static final char[] hexDigit = {
|
||||
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
|
||||
};
|
||||
}
|
116
langtools/src/share/classes/com/sun/tools/sjavac/CopyFile.java
Normal file
116
langtools/src/share/classes/com/sun/tools/sjavac/CopyFile.java
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.*;
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The copy file transform simply copies a matching file from -src to -d .
|
||||
* Such files are typically images, xml documents and other data files.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class CopyFile implements Transformer {
|
||||
|
||||
public void setExtra(String e) {
|
||||
}
|
||||
|
||||
public void setExtra(String[] a) {
|
||||
}
|
||||
|
||||
public boolean transform(Map<String,Set<URI>> pkgSrcs,
|
||||
Set<URI> visibleSrcs,
|
||||
Map<URI,Set<String>> visibleClasses,
|
||||
Map<String,Set<String>> oldPackageDependents,
|
||||
URI destRoot,
|
||||
Map<String,Set<URI>> packageArtifacts,
|
||||
Map<String,Set<String>> packageDependencies,
|
||||
Map<String,String> packagePubapis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
PrintStream out,
|
||||
PrintStream err)
|
||||
{
|
||||
boolean rc = true;
|
||||
String dest_filename;
|
||||
File dest;
|
||||
|
||||
for (String pkgName : pkgSrcs.keySet()) {
|
||||
String pkgNameF = Util.toFileSystemPath(pkgName);
|
||||
for (URI u : pkgSrcs.get(pkgName)) {
|
||||
File src = new File(u);
|
||||
File destDir;
|
||||
destDir = new File(destRoot.getPath()+File.separator+pkgNameF);
|
||||
dest_filename = destRoot.getPath()+File.separator+pkgNameF+File.separator+src.getName();
|
||||
dest = new File(dest_filename);
|
||||
|
||||
if (!destDir.isDirectory()) {
|
||||
if (!destDir.mkdirs()) {
|
||||
Log.error("Error: The copier could not create the directory "+
|
||||
destDir.getPath());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Set<URI> as = packageArtifacts.get(pkgName);
|
||||
if (as == null) {
|
||||
as = new HashSet<URI>();
|
||||
packageArtifacts.put(pkgName, as);
|
||||
}
|
||||
as.add(dest.toURI());
|
||||
|
||||
if (dest.exists() && dest.lastModified() > src.lastModified()) {
|
||||
// A copied file exists, and its timestamp is newer than the source.
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.info("Copying "+pkgNameF+File.separator+src.getName());
|
||||
|
||||
try (InputStream fin = new FileInputStream(src);
|
||||
OutputStream fout = new FileOutputStream(dest)) {
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = fin.read(buf)) > 0){
|
||||
fout.write(buf, 0, len);
|
||||
}
|
||||
}
|
||||
catch(IOException e){
|
||||
Log.error("Could not copy the file "+src.getPath()+" to "+dest.getPath());
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
857
langtools/src/share/classes/com/sun/tools/sjavac/JavacState.java
Normal file
857
langtools/src/share/classes/com/sun/tools/sjavac/JavacState.java
Normal file
@ -0,0 +1,857 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The javac state class maintains the previous (prev) and the current (now)
|
||||
* build states and everything else that goes into the javac_state file.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class JavacState
|
||||
{
|
||||
// The arguments to the compile. If not identical, then it cannot
|
||||
// be an incremental build!
|
||||
String theArgs;
|
||||
// The number of cores limits how many threads are used for heavy concurrent work.
|
||||
int numCores;
|
||||
|
||||
// The bin_dir/javac_state
|
||||
private String javacStateFilename;
|
||||
private File javacState;
|
||||
|
||||
// The previous build state is loaded from javac_state
|
||||
private BuildState prev;
|
||||
// The current build state is constructed during the build,
|
||||
// then saved as the new javac_state.
|
||||
private BuildState now;
|
||||
|
||||
// Something has changed in the javac_state. It needs to be saved!
|
||||
private boolean needsSaving;
|
||||
// If this is a new javac_state file, then do not print unnecessary messages.
|
||||
private boolean newJavacState;
|
||||
|
||||
// These are packages where something has changed and the package
|
||||
// needs to be recompiled. Actions that trigger recompilation:
|
||||
// * source belonging to the package has changed
|
||||
// * artifact belonging to the package is lost, or its timestamp has been changed.
|
||||
// * an unknown artifact has appeared, we simply delete it, but we also trigger a recompilation.
|
||||
// * a package that is tainted, taints all packages that depend on it.
|
||||
private Set<String> taintedPackages;
|
||||
// After a compile, the pubapis are compared with the pubapis stored in the javac state file.
|
||||
// Any packages where the pubapi differ are added to this set.
|
||||
// Later we use this set and the dependency information to taint dependent packages.
|
||||
private Set<String> packagesWithChangedPublicApis;
|
||||
// When a module-info.java file is changed, taint the module,
|
||||
// then taint all modules that depend on that that module.
|
||||
// A module dependency can occur directly through a require, or
|
||||
// indirectly through a module that does a public export for the first tainted module.
|
||||
// When all modules are tainted, then taint all packages belonging to these modules.
|
||||
// Then rebuild. It is perhaps possible (and valuable?) to do a more finegrained examination of the
|
||||
// change in module-info.java, but that will have to wait.
|
||||
private Set<String> taintedModules;
|
||||
// The set of all packages that has been recompiled.
|
||||
// Copy over the javac_state for the packages that did not need recompilation,
|
||||
// verbatim from the previous (prev) to the new (now) build state.
|
||||
private Set<String> recompiledPackages;
|
||||
|
||||
// The output directories filled with tasty artifacts.
|
||||
private File binDir, gensrcDir, headerDir;
|
||||
|
||||
// The current status of the file system.
|
||||
private Set<File> binArtifacts;
|
||||
private Set<File> gensrcArtifacts;
|
||||
private Set<File> headerArtifacts;
|
||||
|
||||
// The status of the sources.
|
||||
Set<Source> removedSources = null;
|
||||
Set<Source> addedSources = null;
|
||||
Set<Source> modifiedSources = null;
|
||||
|
||||
// Visible sources for linking. These are the only
|
||||
// ones that -sourcepath is allowed to see.
|
||||
Set<URI> visibleSrcs;
|
||||
|
||||
// Visible classes for linking. These are the only
|
||||
// ones that -classpath is allowed to see.
|
||||
// It maps from a classpath root to the set of visible classes for that root.
|
||||
// If the set is empty, then all classes are visible for that root.
|
||||
// It can also map from a jar file to the set of visible classes for that jar file.
|
||||
Map<URI,Set<String>> visibleClasses;
|
||||
|
||||
// Setup two transforms that always exist.
|
||||
private CopyFile copyFiles = new CopyFile();
|
||||
private CompileJavaPackages compileJavaPackages = new CompileJavaPackages();
|
||||
|
||||
// Where to send stdout and stderr.
|
||||
private PrintStream out, err;
|
||||
|
||||
JavacState(String[] args, File bd, File gd, File hd, boolean permitUnidentifiedArtifacts, boolean removeJavacState,
|
||||
PrintStream o, PrintStream e) {
|
||||
out = o;
|
||||
err = e;
|
||||
numCores = Main.findNumberOption(args, "-j");
|
||||
theArgs = "";
|
||||
for (String a : removeArgsNotAffectingState(args)) {
|
||||
theArgs = theArgs+a+" ";
|
||||
}
|
||||
binDir = bd;
|
||||
gensrcDir = gd;
|
||||
headerDir = hd;
|
||||
javacStateFilename = binDir.getPath()+File.separator+"javac_state";
|
||||
javacState = new File(javacStateFilename);
|
||||
if (removeJavacState && javacState.exists()) {
|
||||
javacState.delete();
|
||||
}
|
||||
newJavacState = false;
|
||||
if (!javacState.exists()) {
|
||||
newJavacState = true;
|
||||
// If there is no javac_state then delete the contents of all the artifact dirs!
|
||||
// We do not want to risk building a broken incremental build.
|
||||
// BUT since the makefiles still copy things straight into the bin_dir et al,
|
||||
// we avoid deleting files here, if the option --permit-unidentified-classes was supplied.
|
||||
if (!permitUnidentifiedArtifacts) {
|
||||
deleteContents(binDir);
|
||||
deleteContents(gensrcDir);
|
||||
deleteContents(headerDir);
|
||||
}
|
||||
needsSaving = true;
|
||||
}
|
||||
prev = new BuildState();
|
||||
now = new BuildState();
|
||||
taintedPackages = new HashSet<String>();
|
||||
recompiledPackages = new HashSet<String>();
|
||||
packagesWithChangedPublicApis = new HashSet<String>();
|
||||
}
|
||||
|
||||
public BuildState prev() { return prev; }
|
||||
public BuildState now() { return now; }
|
||||
|
||||
/**
|
||||
* Remove args not affecting the state.
|
||||
*/
|
||||
static String[] removeArgsNotAffectingState(String[] args) {
|
||||
String[] out = new String[args.length];
|
||||
int j = 0;
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals("-j")) {
|
||||
// Just skip it and skip following value
|
||||
i++;
|
||||
} else if (args[i].startsWith("--server:")) {
|
||||
// Just skip it.
|
||||
} else if (args[i].startsWith("--log=")) {
|
||||
// Just skip it.
|
||||
} else if (args[i].equals("--compare-found-sources")) {
|
||||
// Just skip it and skip verify file name
|
||||
i++;
|
||||
} else {
|
||||
// Copy argument.
|
||||
out[j] = args[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
String[] ret = new String[j];
|
||||
System.arraycopy(out, 0, ret, 0, j);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify which sources are visible to the compiler through -sourcepath.
|
||||
*/
|
||||
public void setVisibleSources(Map<String,Source> vs) {
|
||||
visibleSrcs = new HashSet<URI>();
|
||||
for (String s : vs.keySet()) {
|
||||
Source src = vs.get(s);
|
||||
visibleSrcs.add(src.file().toURI());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify which classes are visible to the compiler through -classpath.
|
||||
*/
|
||||
public void setVisibleClasses(Map<String,Source> vs) {
|
||||
visibleSrcs = new HashSet<URI>();
|
||||
for (String s : vs.keySet()) {
|
||||
Source src = vs.get(s);
|
||||
visibleSrcs.add(src.file().toURI());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns true if this is an incremental build.
|
||||
*/
|
||||
public boolean isIncremental() {
|
||||
return !prev.sources().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all artifacts that exists on disk.
|
||||
*/
|
||||
public void findAllArtifacts() {
|
||||
binArtifacts = findAllFiles(binDir);
|
||||
gensrcArtifacts = findAllFiles(gensrcDir);
|
||||
headerArtifacts = findAllFiles(headerDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the artifacts generated for this package in the previous build.
|
||||
*/
|
||||
private Map<String,File> fetchPrevArtifacts(String pkg) {
|
||||
Package p = prev.packages().get(pkg);
|
||||
if (p != null) {
|
||||
return p.artifacts();
|
||||
}
|
||||
return new HashMap<String,File>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all prev artifacts in the currently tainted packages.
|
||||
*/
|
||||
public void deleteClassArtifactsInTaintedPackages() {
|
||||
for (String pkg : taintedPackages) {
|
||||
Map<String,File> arts = fetchPrevArtifacts(pkg);
|
||||
for (File f : arts.values()) {
|
||||
if (f.exists() && f.getName().endsWith(".class")) {
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the javac_state file to be in need of saving and as a side effect,
|
||||
* it gets a new timestamp.
|
||||
*/
|
||||
private void needsSaving() {
|
||||
needsSaving = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the javac_state file.
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
if (!needsSaving) return;
|
||||
try (FileWriter out = new FileWriter(javacStateFilename)) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
long millisNow = System.currentTimeMillis();
|
||||
Date d = new Date(millisNow);
|
||||
SimpleDateFormat df =
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
|
||||
b.append("# javac_state ver 0.3 generated "+millisNow+" "+df.format(d)+"\n");
|
||||
b.append("# This format might change at any time. Please do not depend on it.\n");
|
||||
b.append("# M module\n");
|
||||
b.append("# P package\n");
|
||||
b.append("# S C source_tobe_compiled timestamp\n");
|
||||
b.append("# S L link_only_source timestamp\n");
|
||||
b.append("# G C generated_source timestamp\n");
|
||||
b.append("# A artifact timestamp\n");
|
||||
b.append("# D dependency\n");
|
||||
b.append("# I pubapi\n");
|
||||
b.append("# R arguments\n");
|
||||
b.append("R ").append(theArgs).append("\n");
|
||||
|
||||
// Copy over the javac_state for the packages that did not need recompilation.
|
||||
now.copyPackagesExcept(prev, recompiledPackages, new HashSet<String>());
|
||||
// Save the packages, ie package names, dependencies, pubapis and artifacts!
|
||||
// I.e. the lot.
|
||||
Module.saveModules(now.modules(), b);
|
||||
|
||||
String s = b.toString();
|
||||
out.write(s, 0, s.length());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a javac_state file.
|
||||
*/
|
||||
public static JavacState load(String[] args, File binDir, File gensrcDir, File headerDir,
|
||||
boolean permitUnidentifiedArtifacts, PrintStream out, PrintStream err) {
|
||||
JavacState db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, false, out, err);
|
||||
Module lastModule = null;
|
||||
Package lastPackage = null;
|
||||
Source lastSource = null;
|
||||
boolean noFileFound = false;
|
||||
boolean foundCorrectVerNr = false;
|
||||
boolean newCommandLine = false;
|
||||
boolean syntaxError = false;
|
||||
|
||||
try (BufferedReader in = new BufferedReader(new FileReader(db.javacStateFilename))) {
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l==null) break;
|
||||
if (l.length()>=3 && l.charAt(1) == ' ') {
|
||||
char c = l.charAt(0);
|
||||
if (c == 'M') {
|
||||
lastModule = db.prev.loadModule(l);
|
||||
} else
|
||||
if (c == 'P') {
|
||||
if (lastModule == null) { syntaxError = true; break; }
|
||||
lastPackage = db.prev.loadPackage(lastModule, l);
|
||||
} else
|
||||
if (c == 'D') {
|
||||
if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
|
||||
lastPackage.loadDependency(l);
|
||||
} else
|
||||
if (c == 'I') {
|
||||
if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
|
||||
lastPackage.loadPubapi(l);
|
||||
} else
|
||||
if (c == 'A') {
|
||||
if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
|
||||
lastPackage.loadArtifact(l);
|
||||
} else
|
||||
if (c == 'S') {
|
||||
if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
|
||||
lastSource = db.prev.loadSource(lastPackage, l, false);
|
||||
} else
|
||||
if (c == 'G') {
|
||||
if (lastModule == null || lastPackage == null) { syntaxError = true; break; }
|
||||
lastSource = db.prev.loadSource(lastPackage, l, true);
|
||||
} else
|
||||
if (c == 'R') {
|
||||
String ncmdl = "R "+db.theArgs;
|
||||
if (!l.equals(ncmdl)) {
|
||||
newCommandLine = true;
|
||||
}
|
||||
} else
|
||||
if (c == '#') {
|
||||
if (l.startsWith("# javac_state ver ")) {
|
||||
int sp = l.indexOf(" ", 18);
|
||||
if (sp != -1) {
|
||||
String ver = l.substring(18,sp);
|
||||
if (!ver.equals("0.3")) {
|
||||
break;
|
||||
}
|
||||
foundCorrectVerNr = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// Silently create a new javac_state file.
|
||||
noFileFound = true;
|
||||
} catch (IOException e) {
|
||||
Log.info("Dropping old javac_state because of errors when reading it.");
|
||||
db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err);
|
||||
foundCorrectVerNr = true;
|
||||
newCommandLine = false;
|
||||
syntaxError = false;
|
||||
}
|
||||
if (foundCorrectVerNr == false && !noFileFound) {
|
||||
Log.info("Dropping old javac_state since it is of an old version.");
|
||||
db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err);
|
||||
} else
|
||||
if (newCommandLine == true && !noFileFound) {
|
||||
Log.info("Dropping old javac_state since a new command line is used!");
|
||||
db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err);
|
||||
} else
|
||||
if (syntaxError == true) {
|
||||
Log.info("Dropping old javac_state since it contains syntax errors.");
|
||||
db = new JavacState(args, binDir, gensrcDir, headerDir, permitUnidentifiedArtifacts, true, out, err);
|
||||
}
|
||||
db.prev.calculateDependents();
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a java package as tainted, ie it needs recompilation.
|
||||
*/
|
||||
public void taintPackage(String name, String because) {
|
||||
if (!taintedPackages.contains(name)) {
|
||||
if (because != null) Log.debug("Tainting "+Util.justPackageName(name)+" because "+because);
|
||||
// It has not been tainted before.
|
||||
taintedPackages.add(name);
|
||||
needsSaving();
|
||||
Package nowp = now.packages().get(name);
|
||||
if (nowp != null) {
|
||||
for (String d : nowp.dependents()) {
|
||||
taintPackage(d, because);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This packages need recompilation.
|
||||
*/
|
||||
public Set<String> taintedPackages() {
|
||||
return taintedPackages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean out the tainted package set, used after the first round of compiles,
|
||||
* prior to propagating dependencies.
|
||||
*/
|
||||
public void clearTaintedPackages() {
|
||||
taintedPackages = new HashSet<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through all sources and check which have been removed, added or modified
|
||||
* and taint the corresponding packages.
|
||||
*/
|
||||
public void checkSourceStatus(boolean check_gensrc) {
|
||||
removedSources = calculateRemovedSources();
|
||||
for (Source s : removedSources) {
|
||||
if (!s.isGenerated() || check_gensrc) {
|
||||
taintPackage(s.pkg().name(), "source "+s.name()+" was removed");
|
||||
}
|
||||
}
|
||||
|
||||
addedSources = calculateAddedSources();
|
||||
for (Source s : addedSources) {
|
||||
String msg = null;
|
||||
if (isIncremental()) {
|
||||
// When building from scratch, there is no point
|
||||
// printing "was added" for every file since all files are added.
|
||||
// However for an incremental build it makes sense.
|
||||
msg = "source "+s.name()+" was added";
|
||||
}
|
||||
if (!s.isGenerated() || check_gensrc) {
|
||||
taintPackage(s.pkg().name(), msg);
|
||||
}
|
||||
}
|
||||
|
||||
modifiedSources = calculateModifiedSources();
|
||||
for (Source s : modifiedSources) {
|
||||
if (!s.isGenerated() || check_gensrc) {
|
||||
taintPackage(s.pkg().name(), "source "+s.name()+" was modified");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the compile_java_packages suffix rule for .java files.
|
||||
*/
|
||||
public Map<String,Transformer> getJavaSuffixRule() {
|
||||
Map<String,Transformer> sr = new HashMap<String,Transformer>();
|
||||
sr.put(".java", compileJavaPackages);
|
||||
return sr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the copying transform.
|
||||
*/
|
||||
public Transformer getCopier() {
|
||||
return copyFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* If artifacts have gone missing, force a recompile of the packages
|
||||
* they belong to.
|
||||
*/
|
||||
public void taintPackagesThatMissArtifacts() {
|
||||
for (Package pkg : prev.packages().values()) {
|
||||
for (File f : pkg.artifacts().values()) {
|
||||
if (!f.exists()) {
|
||||
// Hmm, the artifact on disk does not exist! Someone has removed it....
|
||||
// Lets rebuild the package.
|
||||
taintPackage(pkg.name(), ""+f+" is missing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate recompilation through the dependency chains.
|
||||
* Avoid re-tainting packages that have already been compiled.
|
||||
*/
|
||||
public void taintPackagesDependingOnChangedPackages(Set<String> pkgs, Set<String> recentlyCompiled) {
|
||||
for (Package pkg : prev.packages().values()) {
|
||||
for (String dep : pkg.dependencies()) {
|
||||
if (pkgs.contains(dep) && !recentlyCompiled.contains(pkg.name())) {
|
||||
taintPackage(pkg.name(), " its depending on "+dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan all output dirs for artifacts and remove those files (artifacts?)
|
||||
* that are not recognized as such, in the javac_state file.
|
||||
*/
|
||||
public void removeUnidentifiedArtifacts() {
|
||||
Set<File> allKnownArtifacts = new HashSet<File>();
|
||||
for (Package pkg : prev.packages().values()) {
|
||||
for (File f : pkg.artifacts().values()) {
|
||||
allKnownArtifacts.add(f);
|
||||
}
|
||||
}
|
||||
// Do not forget about javac_state....
|
||||
allKnownArtifacts.add(javacState);
|
||||
|
||||
for (File f : binArtifacts) {
|
||||
if (!allKnownArtifacts.contains(f)) {
|
||||
Log.debug("Removing "+f.getPath()+" since it is unknown to the javac_state.");
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
for (File f : headerArtifacts) {
|
||||
if (!allKnownArtifacts.contains(f)) {
|
||||
Log.debug("Removing "+f.getPath()+" since it is unknown to the javac_state.");
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
for (File f : gensrcArtifacts) {
|
||||
if (!allKnownArtifacts.contains(f)) {
|
||||
Log.debug("Removing "+f.getPath()+" since it is unknown to the javac_state.");
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove artifacts that are no longer produced when compiling!
|
||||
*/
|
||||
public void removeSuperfluousArtifacts(Set<String> recentlyCompiled) {
|
||||
// Nothing to do, if nothing was recompiled.
|
||||
if (recentlyCompiled.size() == 0) return;
|
||||
|
||||
for (String pkg : now.packages().keySet()) {
|
||||
// If this package has not been recompiled, skip the check.
|
||||
if (!recentlyCompiled.contains(pkg)) continue;
|
||||
Collection<File> arts = now.artifacts().values();
|
||||
for (File f : fetchPrevArtifacts(pkg).values()) {
|
||||
if (!arts.contains(f)) {
|
||||
Log.debug("Removing "+f.getPath()+" since it is now superfluous!");
|
||||
if (f.exists()) f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return those files belonging to prev, but not now.
|
||||
*/
|
||||
private Set<Source> calculateRemovedSources() {
|
||||
Set<Source> removed = new HashSet<Source>();
|
||||
for (String src : prev.sources().keySet()) {
|
||||
if (now.sources().get(src) == null) {
|
||||
removed.add(prev.sources().get(src));
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return those files belonging to now, but not prev.
|
||||
*/
|
||||
private Set<Source> calculateAddedSources() {
|
||||
Set<Source> added = new HashSet<Source>();
|
||||
for (String src : now.sources().keySet()) {
|
||||
if (prev.sources().get(src) == null) {
|
||||
added.add(now.sources().get(src));
|
||||
}
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return those files where the timestamp is newer.
|
||||
* If a source file timestamp suddenly is older than what is known
|
||||
* about it in javac_state, then consider it modified, but print
|
||||
* a warning!
|
||||
*/
|
||||
private Set<Source> calculateModifiedSources() {
|
||||
Set<Source> modified = new HashSet<Source>();
|
||||
for (String src : now.sources().keySet()) {
|
||||
Source n = now.sources().get(src);
|
||||
Source t = prev.sources().get(src);
|
||||
if (prev.sources().get(src) != null) {
|
||||
if (t != null) {
|
||||
if (n.lastModified() > t.lastModified()) {
|
||||
modified.add(n);
|
||||
} else if (n.lastModified() < t.lastModified()) {
|
||||
modified.add(n);
|
||||
Log.warn("The source file "+n.name()+" timestamp has moved backwards in time.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively delete a directory and all its contents.
|
||||
*/
|
||||
private static void deleteContents(File dir) {
|
||||
if (dir != null && dir.exists()) {
|
||||
for (File f : dir.listFiles()) {
|
||||
if (f.isDirectory()) {
|
||||
deleteContents(f);
|
||||
}
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the copy translator only.
|
||||
*/
|
||||
public void performCopying(File binDir, Map<String,Transformer> suffixRules) {
|
||||
Map<String,Transformer> sr = new HashMap<String,Transformer>();
|
||||
for (Map.Entry<String,Transformer> e : suffixRules.entrySet()) {
|
||||
if (e.getValue() == copyFiles) {
|
||||
sr.put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
perform(binDir, sr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all the translators that translate into java source code.
|
||||
* I.e. all translators that are not copy nor compile_java_source.
|
||||
*/
|
||||
public void performTranslation(File gensrcDir, Map<String,Transformer> suffixRules) {
|
||||
Map<String,Transformer> sr = new HashMap<String,Transformer>();
|
||||
for (Map.Entry<String,Transformer> e : suffixRules.entrySet()) {
|
||||
if (e.getValue() != copyFiles &&
|
||||
e.getValue() != compileJavaPackages) {
|
||||
sr.put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
perform(gensrcDir, sr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile all the java sources. Return true, if it needs to be called again!
|
||||
*/
|
||||
public boolean performJavaCompilations(File binDir,
|
||||
String serverSettings,
|
||||
String[] args,
|
||||
Set<String> recentlyCompiled,
|
||||
boolean[] rcValue) {
|
||||
Map<String,Transformer> suffixRules = new HashMap<String,Transformer>();
|
||||
suffixRules.put(".java", compileJavaPackages);
|
||||
compileJavaPackages.setExtra(serverSettings);
|
||||
compileJavaPackages.setExtra(args);
|
||||
|
||||
rcValue[0] = perform(binDir, suffixRules);
|
||||
recentlyCompiled.addAll(taintedPackages());
|
||||
clearTaintedPackages();
|
||||
boolean again = !packagesWithChangedPublicApis.isEmpty();
|
||||
taintPackagesDependingOnChangedPackages(packagesWithChangedPublicApis, recentlyCompiled);
|
||||
packagesWithChangedPublicApis = new HashSet<String>();
|
||||
return again && rcValue[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the source into the set of sources belonging to the given transform.
|
||||
*/
|
||||
private void addFileToTransform(Map<Transformer,Map<String,Set<URI>>> gs, Transformer t, Source s) {
|
||||
Map<String,Set<URI>> fs = gs.get(t);
|
||||
if (fs == null) {
|
||||
fs = new HashMap<String,Set<URI>>();
|
||||
gs.put(t, fs);
|
||||
}
|
||||
Set<URI> ss = fs.get(s.pkg().name());
|
||||
if (ss == null) {
|
||||
ss = new HashSet<URI>();
|
||||
fs.put(s.pkg().name(), ss);
|
||||
}
|
||||
ss.add(s.file().toURI());
|
||||
}
|
||||
|
||||
/**
|
||||
* For all packages, find all sources belonging to the package, group the sources
|
||||
* based on their transformers and apply the transformers on each source code group.
|
||||
*/
|
||||
private boolean perform(File outputDir, Map<String,Transformer> suffixRules)
|
||||
{
|
||||
boolean rc = true;
|
||||
// Group sources based on transforms. A source file can only belong to a single transform.
|
||||
Map<Transformer,Map<String,Set<URI>>> groupedSources = new HashMap<Transformer,Map<String,Set<URI>>>();
|
||||
for (Source src : now.sources().values()) {
|
||||
Transformer t = suffixRules.get(src.suffix());
|
||||
if (t != null) {
|
||||
if (taintedPackages.contains(src.pkg().name()) && !src.isLinkedOnly()) {
|
||||
addFileToTransform(groupedSources, t, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Go through the transforms and transform them.
|
||||
for (Map.Entry<Transformer,Map<String,Set<URI>>> e : groupedSources.entrySet()) {
|
||||
Transformer t = e.getKey();
|
||||
Map<String,Set<URI>> srcs = e.getValue();
|
||||
// These maps need to be synchronized since multiple threads will be writing results into them.
|
||||
Map<String,Set<URI>> packageArtifacts = Collections.synchronizedMap(new HashMap<String,Set<URI>>());
|
||||
Map<String,Set<String>> packageDependencies = Collections.synchronizedMap(new HashMap<String,Set<String>>());
|
||||
Map<String,String> packagePublicApis = Collections.synchronizedMap(new HashMap<String,String>());
|
||||
|
||||
boolean r = t.transform(srcs,
|
||||
visibleSrcs,
|
||||
visibleClasses,
|
||||
prev.dependents(),
|
||||
outputDir.toURI(),
|
||||
packageArtifacts,
|
||||
packageDependencies,
|
||||
packagePublicApis,
|
||||
0,
|
||||
isIncremental(),
|
||||
numCores,
|
||||
out,
|
||||
err);
|
||||
if (!r) rc = false;
|
||||
|
||||
for (String p : srcs.keySet()) {
|
||||
recompiledPackages.add(p);
|
||||
}
|
||||
// The transform is done! Extract all the artifacts and store the info into the Package objects.
|
||||
for (Map.Entry<String,Set<URI>> a : packageArtifacts.entrySet()) {
|
||||
Module mnow = now.findModuleFromPackageName(a.getKey());
|
||||
mnow.addArtifacts(a.getKey(), a.getValue());
|
||||
}
|
||||
// Extract all the dependencies and store the info into the Package objects.
|
||||
for (Map.Entry<String,Set<String>> a : packageDependencies.entrySet()) {
|
||||
Set<String> deps = a.getValue();
|
||||
Module mnow = now.findModuleFromPackageName(a.getKey());
|
||||
mnow.setDependencies(a.getKey(), deps);
|
||||
}
|
||||
// Extract all the pubapis and store the info into the Package objects.
|
||||
for (Map.Entry<String,String> a : packagePublicApis.entrySet()) {
|
||||
Module mprev = prev.findModuleFromPackageName(a.getKey());
|
||||
List<String> pubapi = Package.pubapiToList(a.getValue());
|
||||
Module mnow = now.findModuleFromPackageName(a.getKey());
|
||||
mnow.setPubapi(a.getKey(), pubapi);
|
||||
if (mprev.hasPubapiChanged(a.getKey(), pubapi)) {
|
||||
// Aha! The pubapi of this package has changed!
|
||||
// It can also be a new compile from scratch.
|
||||
if (mprev.lookupPackage(a.getKey()).existsInJavacState()) {
|
||||
// This is an incremental compile! The pubapi
|
||||
// did change. Trigger recompilation of dependents.
|
||||
packagesWithChangedPublicApis.add(a.getKey());
|
||||
Log.info("The pubapi of "+Util.justPackageName(a.getKey())+" has changed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to recursively find all files below a directory.
|
||||
*/
|
||||
private static Set<File> findAllFiles(File dir) {
|
||||
Set<File> foundFiles = new HashSet<File>();
|
||||
if (dir == null) {
|
||||
return foundFiles;
|
||||
}
|
||||
recurse(dir, foundFiles);
|
||||
return foundFiles;
|
||||
}
|
||||
|
||||
private static void recurse(File dir, Set<File> foundFiles) {
|
||||
for (File f : dir.listFiles()) {
|
||||
if (f.isFile()) {
|
||||
foundFiles.add(f);
|
||||
} else if (f.isDirectory()) {
|
||||
recurse(f, foundFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the calculate source list, with an explicit list, usually supplied from the makefile.
|
||||
* Used to detect bugs where the makefile and sjavac have different opinions on which files
|
||||
* should be compiled.
|
||||
*/
|
||||
public void compareWithMakefileList(File makefileSourceList)
|
||||
throws ProblemException
|
||||
{
|
||||
// If we are building on win32 using for example cygwin the paths in the makefile source list
|
||||
// might be /cygdrive/c/.... which does not match c:\....
|
||||
// We need to adjust our calculated sources to be identical, if necessary.
|
||||
boolean mightNeedRewriting = File.pathSeparatorChar == ';';
|
||||
|
||||
if (makefileSourceList == null) return;
|
||||
|
||||
Set<String> calculatedSources = new HashSet<String>();
|
||||
Set<String> listedSources = new HashSet<String>();
|
||||
|
||||
// Create a set of filenames with full paths.
|
||||
for (Source s : now.sources().values()) {
|
||||
calculatedSources.add(s.file().getPath());
|
||||
}
|
||||
// Read in the file and create another set of filenames with full paths.
|
||||
try {
|
||||
BufferedReader in = new BufferedReader(new FileReader(makefileSourceList));
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l==null) break;
|
||||
l = l.trim();
|
||||
if (mightNeedRewriting) {
|
||||
if (l.indexOf(":") == 1 && l.indexOf("\\") == 2) {
|
||||
// Everything a-ok, the format is already C:\foo\bar
|
||||
} else if (l.indexOf(":") == 1 && l.indexOf("/") == 2) {
|
||||
// The format is C:/foo/bar, rewrite into the above format.
|
||||
l = l.replaceAll("/","\\\\");
|
||||
} else if (l.charAt(0) == '/' && l.indexOf("/",1) != -1) {
|
||||
// The format might be: /cygdrive/c/foo/bar, rewrite into the above format.
|
||||
// Do not hardcode the name cygdrive here.
|
||||
int slash = l.indexOf("/",1);
|
||||
l = l.replaceAll("/","\\\\");
|
||||
l = ""+l.charAt(slash+1)+":"+l.substring(slash+2);
|
||||
}
|
||||
if (Character.isLowerCase(l.charAt(0))) {
|
||||
l = Character.toUpperCase(l.charAt(0))+l.substring(1);
|
||||
}
|
||||
}
|
||||
listedSources.add(l);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new ProblemException("Could not open "+makefileSourceList.getPath()+" since it does not exist!");
|
||||
} catch (IOException e) {
|
||||
throw new ProblemException("Could not read "+makefileSourceList.getPath());
|
||||
}
|
||||
|
||||
for (String s : listedSources) {
|
||||
if (!calculatedSources.contains(s)) {
|
||||
throw new ProblemException("The makefile listed source "+s+" was not calculated by the smart javac wrapper!");
|
||||
}
|
||||
}
|
||||
|
||||
for (String s : calculatedSources) {
|
||||
if (!listedSources.contains(s)) {
|
||||
throw new ProblemException("The smart javac wrapper calculated source "+s+" was not listed by the makefiles!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
92
langtools/src/share/classes/com/sun/tools/sjavac/Log.java
Normal file
92
langtools/src/share/classes/com/sun/tools/sjavac/Log.java
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.PrintStream;
|
||||
|
||||
/**
|
||||
* Utility class only for sjavac logging.
|
||||
* The log level can be set using for example --log=DEBUG on the sjavac command line.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class Log {
|
||||
private static PrintStream out, err;
|
||||
|
||||
public final static int WARN = 1;
|
||||
public final static int INFO = 2;
|
||||
public final static int DEBUG = 3;
|
||||
public final static int TRACE = 4;
|
||||
private static int level = WARN;
|
||||
|
||||
static public void trace(String msg) {
|
||||
if (level >= TRACE) {
|
||||
out.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static public void debug(String msg) {
|
||||
if (level >= DEBUG) {
|
||||
out.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static public void info(String msg) {
|
||||
if (level >= INFO) {
|
||||
out.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static public void warn(String msg) {
|
||||
err.println(msg);
|
||||
}
|
||||
|
||||
static public void error(String msg) {
|
||||
err.println(msg);
|
||||
}
|
||||
|
||||
static public void setLogLevel(String l, PrintStream o, PrintStream e)
|
||||
throws ProblemException {
|
||||
out = o;
|
||||
err = e;
|
||||
if (l.equals("warn")) level = WARN;
|
||||
else if (l.equals("info")) level = INFO;
|
||||
else if (l.equals("debug")) level = DEBUG;
|
||||
else if (l.equals("trace")) level = TRACE;
|
||||
else throw new ProblemException("No such log level \""+l+"\"");
|
||||
}
|
||||
|
||||
static public boolean isTracing() {
|
||||
return level >= TRACE;
|
||||
}
|
||||
|
||||
static public boolean isDebugging() {
|
||||
return level >= DEBUG;
|
||||
}
|
||||
}
|
969
langtools/src/share/classes/com/sun/tools/sjavac/Main.java
Normal file
969
langtools/src/share/classes/com/sun/tools/sjavac/Main.java
Normal file
@ -0,0 +1,969 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import com.sun.tools.sjavac.server.JavacServer;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The main class of the smart javac wrapper tool.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
/* This is a smart javac wrapper primarily used when building the OpenJDK,
|
||||
though other projects are welcome to use it too. But please be aware
|
||||
that it is not an official api and will change in the future.
|
||||
(We really mean it!)
|
||||
|
||||
Goals:
|
||||
|
||||
** Create a state file, containing information about the build, so
|
||||
that incremental builds only rebuild what is necessary. Also the
|
||||
state file can be used by make/ant to detect when to trigger
|
||||
a call to the smart javac wrapper.
|
||||
|
||||
This file is called bin/javac_state (assuming that you specified "-d bin")
|
||||
Thus the simplest makefile is:
|
||||
|
||||
SJAVAC=java -cp .../tools.jar com.sun.tools.sjavac.Main
|
||||
SRCS=$(shell find src -name "*.java")
|
||||
bin/javac_state : $(SRCS)
|
||||
$(SJAVAC) src -d bin
|
||||
|
||||
This makefile will run very fast and detect properly when Java code needs to
|
||||
be recompiled. The smart javac wrapper will then use the information in java_state
|
||||
to do an efficient incremental compile.
|
||||
|
||||
Previously it was near enough impossible to write an efficient makefile for Java
|
||||
with support for incremental builds and dependency tracking.
|
||||
|
||||
** Separate java sources to be compiled from java
|
||||
sources used >only< for linking. The options:
|
||||
|
||||
"dir" points to root dir with sources to be compiled
|
||||
"-sourcepath dir" points to root dir with sources used only for linking
|
||||
"-classpath dir" points to dir with classes used only for linking (as before)
|
||||
|
||||
** Use all cores for compilation by default.
|
||||
"-j 4" limit the number of cores to 4.
|
||||
For the moment, the sjavac server additionally limits the number of cores to three.
|
||||
This will improve in the future when more sharing is performed between concurrent JavaCompilers.
|
||||
|
||||
** Basic translation support from other sources to java, and then compilation of the generated java.
|
||||
This functionality might be moved into annotation processors instead.
|
||||
Again this is driven by the OpenJDK sources where properties and a few other types of files
|
||||
are converted into Java sources regularily. The javac_state embraces copy and tr, and perform
|
||||
incremental recompiles and copying for these as well. META-INF will be a special copy rule
|
||||
that will copy any files found below any META-INF dir in src to the bin/META-INF dir.
|
||||
"-copy .gif"
|
||||
"-copy META-INF"
|
||||
"-tr .prop=com.sun.tools.javac.smart.CompileProperties
|
||||
"-tr .propp=com.sun.tools.javac.smart.CompileProperties,java.util.ListResourceBundle
|
||||
"-tr .proppp=com.sun.tools.javac.smart.CompileProperties,sun.util.resources.LocaleNamesBundle
|
||||
|
||||
** Control which classes in the src,sourcepath and classpath that javac is allowed to see.
|
||||
Again, this is necessary to deal with the source code structure of the OpenJDK which is
|
||||
intricate (read messy).
|
||||
|
||||
"-i tools.*" to include the tools package and all its subpackages in the build.
|
||||
"-x tools.net.aviancarrier.*" to exclude the aviancarrier package and all its sources and subpackages.
|
||||
"-x tools.net.drums" to exclude the drums package only, keep its subpackages.
|
||||
"-xf tools/net/Bar.java" // Do not compile this file...
|
||||
"-xf *Bor.java" // Do not compile Bor.java wherever it is found, BUT do compile ABor.java!
|
||||
"-if tools/net/Bor.java" // Only compile this file...odd, but sometimes used.
|
||||
|
||||
** The smart javac wrapper is driven by the modification time on the source files and compared
|
||||
to the modification times written into the javac_state file.
|
||||
|
||||
It does not compare the modification time of the source with the modification time of the artifact.
|
||||
However it will detect if the modification time of an artifact has changed compared to the java_state,
|
||||
and this will trigger a delete of the artifact and a subsequent recompile of the source.
|
||||
|
||||
The smart javac wrapper is not a generic makefile/ant system. Its purpose is to compile java source
|
||||
as the final step before the output dir is finalized and immediately jared, or jmodded. The output
|
||||
dir should be considered opaque. Do not write into the outputdir yourself!
|
||||
Any artifacts found in the outputdir that javac_state does not know of, will be deleted!
|
||||
This can however be prevented, using the switch --permit-unidentified-artifacts
|
||||
This switch is necessary when build the OpenJDK because its makefiles still write directly to
|
||||
the output classes dirs.
|
||||
|
||||
Any makefile/ant rules that want to put contents into the outputdir should put the content
|
||||
in one of several source roots. Static content that is under version control, can be put in the same source
|
||||
code tree as the Java sources. Dynamic content that is generated by make/ant on the fly, should
|
||||
be put in a separate gensrc_stuff root. The smart javac wrapper call will then take the arguments:
|
||||
"gensrc_stuff src -d bin"
|
||||
|
||||
The command line:
|
||||
java -cp tools.jar com.sun.tools.sjavac.Main \
|
||||
-i "com.bar.*" -x "com.bar.foo.*" \
|
||||
first_root \
|
||||
-i "com.bar.foo.*" \
|
||||
second_root \
|
||||
-x "org.net.*" \
|
||||
-sourcepath link_root_sources \
|
||||
-classpath link_root_classes \
|
||||
-d bin
|
||||
|
||||
Will compile all sources for package com.bar and its subpackages, found below first_root,
|
||||
except the package com.bar.foo (and its subpackages), for which the sources are picked
|
||||
from second_root instead. It will link against classes in link_root_classes and against
|
||||
sources in link_root_sources, but will not see (try to link against) sources matching org.net.*
|
||||
but will link against org.net* classes (if they exist) in link_root_classes.
|
||||
|
||||
(If you want a set of complex filter rules to be applied to several source directories, without
|
||||
having to repeat the the filter rules for each root. You can use the explicit -src option. For example:
|
||||
sjavac -x "com.foo.*" -src root1:root2:root3 )
|
||||
|
||||
The resulting classes are written into bin.
|
||||
*/
|
||||
|
||||
// This is the final destination for classes and copied files.
|
||||
private File bin_dir;
|
||||
// This is where the annotation process will put generated sources.
|
||||
private File gensrc_dir;
|
||||
// This is where javac -h puts the generated c-header files.
|
||||
private File header_dir;
|
||||
|
||||
// This file contains the list of sources genereated by the makefile.
|
||||
// We double check that our calculated list of sources matches this list,
|
||||
// if not, then we terminate with an error!
|
||||
private File makefile_source_list;
|
||||
// The challenging task to manage an incremental build is done by javac_state.
|
||||
private JavacState javac_state;
|
||||
|
||||
// The suffix rules tells you for example, that .java files should be compiled,
|
||||
// and .html files should be copied and .properties files be translated.
|
||||
Map<String,Transformer> suffix_rules;
|
||||
|
||||
public static void main(String... args) {
|
||||
if (args.length > 0 && args[0].startsWith("--startserver:")) {
|
||||
if (args.length>1) {
|
||||
Log.error("When spawning a background server, only a single --startserver argument is allowed.");
|
||||
return;
|
||||
}
|
||||
// Spawn a background server.
|
||||
int rc = JavacServer.startServer(args[0], System.err);
|
||||
System.exit(rc);
|
||||
}
|
||||
Main main = new Main();
|
||||
int rc = main.go(args, System.out, System.err);
|
||||
// Remove the portfile, but only if this background=false was used.
|
||||
JavacServer.cleanup(args);
|
||||
System.exit(rc);
|
||||
}
|
||||
|
||||
private void printHelp() {
|
||||
System.out.println("Usage: sjavac <options>\n"+
|
||||
"where required options are:\n"+
|
||||
"dir Compile all sources in dir recursively\n"+
|
||||
"-d dir Store generated classes here and the javac_state file\n"+
|
||||
"--server:portfile=/tmp/abc Use a background sjavac server\n\n"+
|
||||
"All other arguments as javac, except -implicit:none which is forced by default.\n"+
|
||||
"No java source files can be supplied on the command line, nor can an @file be supplied.\n\n"+
|
||||
"Warning!\n"+
|
||||
"This tool might disappear at any time, and its command line options might change at any time!");
|
||||
}
|
||||
|
||||
public int go(String[] args, PrintStream out, PrintStream err) {
|
||||
try {
|
||||
if (args.length == 0 || findJavaSourceFiles(args) || findAtFile(args) || null==Util.findServerSettings(args)) {
|
||||
printHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Log.setLogLevel(findLogLevel(args), out, err);
|
||||
String server_settings = Util.findServerSettings(args);
|
||||
args = verifyImplicitOption(args);
|
||||
// Find the source root directories, and add the -src option before these, if not there already.
|
||||
args = addSrcBeforeDirectories(args);
|
||||
// Check that there is at least one -src supplied.
|
||||
checkSrcOption(args);
|
||||
// Check that there is one -d supplied.
|
||||
bin_dir = findDirectoryOption(args,"-d","output", true, false, true);
|
||||
gensrc_dir = findDirectoryOption(args,"-s","gensrc", false, false, true);
|
||||
header_dir = findDirectoryOption(args,"-h","headers", false, false, true);
|
||||
makefile_source_list = findFileOption(args,"--compare-found-sources","makefile source list", false);
|
||||
|
||||
// Load the prev build state database.
|
||||
javac_state = JavacState.load(args, bin_dir, gensrc_dir, header_dir,
|
||||
findBooleanOption(args, "--permit-unidentified-artifacts"), out, err);
|
||||
|
||||
// Setup the suffix rules from the command line.
|
||||
suffix_rules = javac_state.getJavaSuffixRule();
|
||||
findTranslateOptions(args, suffix_rules);
|
||||
if (suffix_rules.keySet().size() > 1 && gensrc_dir == null) {
|
||||
Log.error("You have translators but no gensrc dir (-s) specified!");
|
||||
return -1;
|
||||
}
|
||||
findCopyOptions(args, suffix_rules);
|
||||
|
||||
// All found modules are put here.
|
||||
Map<String,Module> modules = new HashMap<String,Module>();
|
||||
// We start out in the legacy empty no-name module.
|
||||
// As soon as we stumble on a module-info.java file we change to that module.
|
||||
Module current_module = new Module("", "");
|
||||
modules.put("", current_module);
|
||||
|
||||
// Find all sources, use the suffix rules to know which files are sources.
|
||||
Map<String,Source> sources = new HashMap<String,Source>();
|
||||
// Find the files, this will automatically populate the found modules
|
||||
// with found packages where the sources are found!
|
||||
findFiles(args, "-src", suffix_rules.keySet(), sources, modules, current_module, false);
|
||||
|
||||
if (sources.isEmpty()) {
|
||||
Log.error("Found nothing to compile!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Find all source files allowable for linking.
|
||||
// We might find more modules here as well.
|
||||
Map<String,Source> sources_to_link_to = new HashMap<String,Source>();
|
||||
// Always reuse -src for linking as well! This means that we might
|
||||
// get two -sourcepath on the commandline after the rewrite, which is
|
||||
// fine. We can have as many as we like. You need to have separate -src/-sourcepath/-classpath
|
||||
// if you need different filtering rules for different roots. If you have the same filtering
|
||||
// rules for all sourcepath roots, you can concatenate them using :(;) as before.
|
||||
rewriteOptions(args, "-src", "-sourcepath");
|
||||
findFiles(args, "-sourcepath", Util.set(".java"), sources_to_link_to, modules, current_module, true);
|
||||
|
||||
// Find all class files allowable for linking.
|
||||
// And pickup knowledge of all modules found here.
|
||||
// This cannot currently filter classes inside jar files.
|
||||
Map<String,Source> classes_to_link_to = new HashMap<String,Source>();
|
||||
// findFiles(args, "-classpath", Util.set(".class"), classes_to_link_to, modules, current_module, true);
|
||||
|
||||
// Find all module sources allowable for linking.
|
||||
Map<String,Source> modules_to_link_to = new HashMap<String,Source>();
|
||||
// findFiles(args, "-modulepath", Util.set(".class"), modules_to_link_to, modules, current_module, true);
|
||||
|
||||
// Add the set of sources to the build database.
|
||||
javac_state.now().collectPackagesSourcesAndArtifacts(modules);
|
||||
javac_state.now().checkInternalState("checking sources", false, sources);
|
||||
javac_state.now().checkInternalState("checking linked sources", true, sources_to_link_to);
|
||||
javac_state.setVisibleSources(sources_to_link_to);
|
||||
|
||||
// If there is any change in the source files, taint packages
|
||||
// and mark the database in need of saving.
|
||||
javac_state.checkSourceStatus(false);
|
||||
|
||||
// Find all existing artifacts. Their timestamp will match the last modified timestamps stored
|
||||
// in javac_state, simply because loading of the JavacState will clean out all artifacts
|
||||
// that do not match the javac_state database.
|
||||
javac_state.findAllArtifacts();
|
||||
|
||||
// Remove unidentified artifacts from the bin, gensrc and header dirs.
|
||||
// (Unless we allow them to be there.)
|
||||
// I.e. artifacts that are not known according to the build database (javac_state).
|
||||
// For examples, files that have been manually copied into these dirs.
|
||||
// Artifacts with bad timestamps (ie the on disk timestamp does not match the timestamp
|
||||
// in javac_state) have already been removed when the javac_state was loaded.
|
||||
if (!findBooleanOption(args, "--permit-unidentified-artifacts")) {
|
||||
javac_state.removeUnidentifiedArtifacts();
|
||||
}
|
||||
// Go through all sources and taint all packages that miss artifacts.
|
||||
javac_state.taintPackagesThatMissArtifacts();
|
||||
|
||||
// Now clean out all known artifacts belonging to tainted packages.
|
||||
javac_state.deleteClassArtifactsInTaintedPackages();
|
||||
// Copy files, for example property files, images files, xml files etc etc.
|
||||
javac_state.performCopying(bin_dir, suffix_rules);
|
||||
// Translate files, for example compile properties or compile idls.
|
||||
javac_state.performTranslation(gensrc_dir, suffix_rules);
|
||||
// Add any potentially generated java sources to the tobe compiled list.
|
||||
// (Generated sources must always have a package.)
|
||||
Map<String,Source> generated_sources = new HashMap<String,Source>();
|
||||
Source.scanRoot(gensrc_dir, Util.set(".java"), null, null, null, null,
|
||||
generated_sources, modules, current_module, false, true, false);
|
||||
javac_state.now().collectPackagesSourcesAndArtifacts(modules);
|
||||
// Recheck the the source files and their timestamps again.
|
||||
javac_state.checkSourceStatus(true);
|
||||
|
||||
// Now do a safety check that the list of source files is identical
|
||||
// to the list Make believes we are compiling. If we do not get this
|
||||
// right, then incremental builds will fail with subtility.
|
||||
// If any difference is detected, then we will fail hard here.
|
||||
// This is an important safety net.
|
||||
javac_state.compareWithMakefileList(makefile_source_list);
|
||||
|
||||
// Do the compilations, repeatedly until no tainted packages exist.
|
||||
boolean again;
|
||||
// Collect the name of all compiled packages.
|
||||
Set<String> recently_compiled = new HashSet<String>();
|
||||
boolean[] rc = new boolean[1];
|
||||
do {
|
||||
// Clean out artifacts in tainted packages.
|
||||
javac_state.deleteClassArtifactsInTaintedPackages();
|
||||
again = javac_state.performJavaCompilations(bin_dir, server_settings, args, recently_compiled, rc);
|
||||
if (!rc[0]) break;
|
||||
} while (again);
|
||||
// Only update the state if the compile went well.
|
||||
if (rc[0]) {
|
||||
javac_state.save();
|
||||
// Collect all the artifacts.
|
||||
javac_state.now().collectArtifacts(modules);
|
||||
// Remove artifacts that were generated during the last compile, but not this one.
|
||||
javac_state.removeSuperfluousArtifacts(recently_compiled);
|
||||
}
|
||||
return rc[0] ? 0 : -1;
|
||||
} catch (ProblemException e) {
|
||||
Log.error(e.getMessage());
|
||||
return -1;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Are java source files passed on the command line?
|
||||
*/
|
||||
private boolean findJavaSourceFiles(String[] args) {
|
||||
String prev = "";
|
||||
for (String s : args) {
|
||||
if (s.endsWith(".java") && !prev.equals("-xf") && !prev.equals("-if")) {
|
||||
return true;
|
||||
}
|
||||
prev = s;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is an at file passed on the command line?
|
||||
*/
|
||||
private boolean findAtFile(String[] args) {
|
||||
for (String s : args) {
|
||||
if (s.startsWith("@")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the log level setting.
|
||||
*/
|
||||
private String findLogLevel(String[] args) {
|
||||
for (String s : args) {
|
||||
if (s.startsWith("--log=") && s.length()>6) {
|
||||
return s.substring(6);
|
||||
}
|
||||
if (s.equals("-verbose")) {
|
||||
return "info";
|
||||
}
|
||||
}
|
||||
return "info";
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove smart javac wrapper arguments, before feeding
|
||||
* the args to the plain javac.
|
||||
*/
|
||||
static String[] removeWrapperArgs(String[] args) {
|
||||
String[] out = new String[args.length];
|
||||
// The first source path index is remembered
|
||||
// here. So that all following can be concatenated to it.
|
||||
int source_path = -1;
|
||||
// The same for class path.
|
||||
int class_path = -1;
|
||||
// And module path.
|
||||
int module_path = -1;
|
||||
int j = 0;
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals("-src") ||
|
||||
args[i].equals("-x") ||
|
||||
args[i].equals("-i") ||
|
||||
args[i].equals("-xf") ||
|
||||
args[i].equals("-if") ||
|
||||
args[i].equals("-copy") ||
|
||||
args[i].equals("-tr") ||
|
||||
args[i].equals("-j")) {
|
||||
// Just skip it and skip following value
|
||||
i++;
|
||||
} else if (args[i].startsWith("--server:")) {
|
||||
// Just skip it.
|
||||
} else if (args[i].startsWith("--log=")) {
|
||||
// Just skip it.
|
||||
} else if (args[i].equals("--permit-unidentified-artifacts")) {
|
||||
// Just skip it.
|
||||
} else if (args[i].equals("--permit-sources-without-package")) {
|
||||
// Just skip it.
|
||||
} else if (args[i].equals("--compare-found-sources")) {
|
||||
// Just skip it and skip verify file name
|
||||
i++;
|
||||
} else if (args[i].equals("-sourcepath")) {
|
||||
if (source_path == -1) {
|
||||
source_path = j;
|
||||
out[j] = args[i];
|
||||
out[j+1] = args[i+1];
|
||||
j+=2;
|
||||
i++;
|
||||
} else {
|
||||
// Skip this and its argument, but
|
||||
// append argument to found sourcepath.
|
||||
out[source_path+1] = out[source_path+1]+File.pathSeparatorChar+args[i+1];
|
||||
i++;
|
||||
}
|
||||
} else if (args[i].equals("-classpath")) {
|
||||
if (class_path == -1) {
|
||||
class_path = j;
|
||||
out[j] = args[i];
|
||||
out[j+1] = args[i+1];
|
||||
j+=2;
|
||||
i++;
|
||||
} else {
|
||||
// Skip this and its argument, but
|
||||
// append argument to found sourcepath.
|
||||
out[class_path+1] = out[class_path+1]+File.pathSeparatorChar+args[i+1];
|
||||
i++;
|
||||
}
|
||||
} else if (args[i].equals("-modulepath")) {
|
||||
if (module_path == -1) {
|
||||
module_path = j;
|
||||
out[j] = args[i];
|
||||
out[j+1] = args[i+1];
|
||||
j+=2;
|
||||
i++;
|
||||
} else {
|
||||
// Skip this and its argument, but
|
||||
// append argument to found sourcepath.
|
||||
out[module_path+1] = out[module_path+1]+File.pathSeparatorChar+args[i+1];
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
// Copy argument.
|
||||
out[j] = args[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
String[] ret = new String[j];
|
||||
System.arraycopy(out, 0, ret, 0, j);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure directory exist, create it if not.
|
||||
*/
|
||||
private static boolean makeSureExists(File dir) {
|
||||
// Make sure the dest directories exist.
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkdirs()) {
|
||||
Log.error("Could not create the directory "+dir.getPath());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a package pattern is valid.
|
||||
*/
|
||||
private static void checkPattern(String s) throws ProblemException {
|
||||
// Package names like foo.bar.gamma are allowed, and
|
||||
// package names suffixed with .* like foo.bar.* are
|
||||
// also allowed.
|
||||
Pattern p = Pattern.compile("[a-zA-Z_]{1}[a-zA-Z0-9_]*(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)*(\\.\\*)?+");
|
||||
Matcher m = p.matcher(s);
|
||||
if (!m.matches()) {
|
||||
throw new ProblemException("The string \""+s+"\" is not a proper package name pattern.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a translate pattern is valid.
|
||||
*/
|
||||
private static void checkTranslatePattern(String s) throws ProblemException {
|
||||
// .prop=com.sun.tools.javac.smart.CompileProperties
|
||||
// .idl=com.sun.corba.CompileIdl
|
||||
// .g3=antlr.CompileGrammar,debug=true
|
||||
Pattern p = Pattern.compile(
|
||||
"\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*=[a-z_]{1}[a-z0-9_]*(\\.[a-z_]{1}[a-z0-9_]*)*"+
|
||||
"(\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*)(,.*)?");
|
||||
Matcher m = p.matcher(s);
|
||||
if (!m.matches()) {
|
||||
throw new ProblemException("The string \""+s+"\" is not a proper translate pattern.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a copy pattern is valid.
|
||||
*/
|
||||
private static void checkCopyPattern(String s) throws ProblemException {
|
||||
// .gif
|
||||
// .html
|
||||
Pattern p = Pattern.compile(
|
||||
"\\.[a-zA-Z_]{1}[a-zA-Z0-9_]*");
|
||||
Matcher m = p.matcher(s);
|
||||
if (!m.matches()) {
|
||||
throw new ProblemException("The string \""+s+"\" is not a proper suffix.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a source file name is valid.
|
||||
*/
|
||||
private static void checkFilePattern(String s) throws ProblemException {
|
||||
// File names like foo/bar/gamma/Bar.java are allowed,
|
||||
// as well as /bar/jndi.properties as well as,
|
||||
// */bar/Foo.java
|
||||
Pattern p = null;
|
||||
if (File.separatorChar == '\\') {
|
||||
p = Pattern.compile("\\*?(.+\\\\)*.+");
|
||||
}
|
||||
else if (File.separatorChar == '/') {
|
||||
p = Pattern.compile("\\*?(.+/)*.+");
|
||||
} else {
|
||||
throw new ProblemException("This platform uses the unsupported "+File.separatorChar+
|
||||
" as file separator character. Please add support for it!");
|
||||
}
|
||||
Matcher m = p.matcher(s);
|
||||
if (!m.matches()) {
|
||||
throw new ProblemException("The string \""+s+"\" is not a proper file name.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the arguments to find an option is used.
|
||||
*/
|
||||
private static boolean hasOption(String[] args, String option) {
|
||||
for (String a : args) {
|
||||
if (a.equals(option)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if -implicit is supplied, if so check that it is none.
|
||||
* If -implicit is not supplied, supply -implicit:none
|
||||
* Only implicit:none is allowed because otherwise the multicore compilations
|
||||
* and dependency tracking will be tangled up.
|
||||
*/
|
||||
private static String[] verifyImplicitOption(String[] args)
|
||||
throws ProblemException {
|
||||
|
||||
boolean foundImplicit = false;
|
||||
for (String a : args) {
|
||||
if (a.startsWith("-implicit:")) {
|
||||
foundImplicit = true;
|
||||
if (!a.equals("-implicit:none")) {
|
||||
throw new ProblemException("The only allowed setting for sjavac is -implicit:none, it is also the default.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (foundImplicit) {
|
||||
return args;
|
||||
}
|
||||
// -implicit:none not found lets add it.
|
||||
String[] newargs = new String[args.length+1];
|
||||
System.arraycopy(args,0, newargs, 0, args.length);
|
||||
newargs[args.length] = "-implicit:none";
|
||||
return newargs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite a single option into something else.
|
||||
*/
|
||||
private static void rewriteOptions(String[] args, String option, String new_option) {
|
||||
for (int i=0; i<args.length; ++i) {
|
||||
if (args[i].equals(option)) {
|
||||
args[i] = new_option;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the arguments to find an option that specifies a directory.
|
||||
* Create the directory if necessary.
|
||||
*/
|
||||
private static File findDirectoryOption(String[] args, String option, String name, boolean needed, boolean allow_dups, boolean create)
|
||||
throws ProblemException, ProblemException {
|
||||
File dir = null;
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals(option)) {
|
||||
if (dir != null) {
|
||||
throw new ProblemException("You have already specified the "+name+" dir!");
|
||||
}
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a directory following "+option+".");
|
||||
}
|
||||
if (args[i+1].indexOf(File.pathSeparatorChar) != -1) {
|
||||
throw new ProblemException("You must only specify a single directory for "+option+".");
|
||||
}
|
||||
dir = new File(args[i+1]);
|
||||
if (!dir.exists()) {
|
||||
if (!create) {
|
||||
throw new ProblemException("This directory does not exist: "+dir.getPath());
|
||||
} else
|
||||
if (!makeSureExists(dir)) {
|
||||
throw new ProblemException("Cannot create directory "+dir.getPath());
|
||||
}
|
||||
}
|
||||
if (!dir.isDirectory()) {
|
||||
throw new ProblemException("\""+args[i+1]+"\" is not a directory.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dir == null && needed) {
|
||||
throw new ProblemException("You have to specify "+option);
|
||||
}
|
||||
try {
|
||||
if (dir != null)
|
||||
return dir.getCanonicalFile();
|
||||
} catch (IOException e) {
|
||||
throw new ProblemException(""+e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Option is followed by path.
|
||||
*/
|
||||
private static boolean shouldBeFollowedByPath(String o) {
|
||||
return o.equals("-s") ||
|
||||
o.equals("-h") ||
|
||||
o.equals("-d") ||
|
||||
o.equals("-sourcepath") ||
|
||||
o.equals("-classpath") ||
|
||||
o.equals("-bootclasspath") ||
|
||||
o.equals("-src");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add -src before source root directories if not already there.
|
||||
*/
|
||||
private static String[] addSrcBeforeDirectories(String[] args) {
|
||||
List<String> newargs = new ArrayList<String>();
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
File dir = new File(args[i]);
|
||||
if (dir.exists() && dir.isDirectory()) {
|
||||
if (i == 0 || !shouldBeFollowedByPath(args[i-1])) {
|
||||
newargs.add("-src");
|
||||
}
|
||||
}
|
||||
newargs.add(args[i]);
|
||||
}
|
||||
return newargs.toArray(new String[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the -src options.
|
||||
*/
|
||||
private static void checkSrcOption(String[] args)
|
||||
throws ProblemException {
|
||||
Set<File> dirs = new HashSet<File>();
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals("-src")) {
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a directory following -src.");
|
||||
}
|
||||
StringTokenizer st = new StringTokenizer(args[i+1], File.pathSeparator);
|
||||
while (st.hasMoreElements()) {
|
||||
File dir = new File(st.nextToken());
|
||||
if (!dir.exists()) {
|
||||
throw new ProblemException("This directory does not exist: "+dir.getPath());
|
||||
}
|
||||
if (!dir.isDirectory()) {
|
||||
throw new ProblemException("\""+dir.getPath()+"\" is not a directory.");
|
||||
}
|
||||
if (dirs.contains(dir)) {
|
||||
throw new ProblemException("The src directory \""+dir.getPath()+"\" is specified more than once!");
|
||||
}
|
||||
dirs.add(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dirs.isEmpty()) {
|
||||
throw new ProblemException("You have to specify -src.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the arguments to find an option that specifies a file.
|
||||
*/
|
||||
private static File findFileOption(String[] args, String option, String name, boolean needed)
|
||||
throws ProblemException, ProblemException {
|
||||
File file = null;
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals(option)) {
|
||||
if (file != null) {
|
||||
throw new ProblemException("You have already specified the "+name+" file!");
|
||||
}
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a file following "+option+".");
|
||||
}
|
||||
file = new File(args[i+1]);
|
||||
if (file.isDirectory()) {
|
||||
throw new ProblemException("\""+args[i+1]+"\" is not a file.");
|
||||
}
|
||||
if (!file.exists() && needed) {
|
||||
throw new ProblemException("The file \""+args[i+1]+"\" does not exist.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (file == null && needed) {
|
||||
throw new ProblemException("You have to specify "+option);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a specific switch, return true if found.
|
||||
*/
|
||||
public static boolean findBooleanOption(String[] args, String option) {
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals(option)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the arguments to find an option that specifies a number.
|
||||
*/
|
||||
public static int findNumberOption(String[] args, String option) {
|
||||
int rc = 0;
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals(option)) {
|
||||
if (args.length > i+1) {
|
||||
rc = Integer.parseInt(args[i+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the arguments to find the option (-tr) that setup translation rules to java source
|
||||
* from different sources. For example: .properties are translated using CompileProperties
|
||||
* The found translators are stored as suffix rules.
|
||||
*/
|
||||
private static void findTranslateOptions(String[] args, Map<String,Transformer> suffix_rules)
|
||||
throws ProblemException, ProblemException {
|
||||
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals("-tr")) {
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a translate rule following -tr.");
|
||||
}
|
||||
String s = args[i+1];
|
||||
checkTranslatePattern(s);
|
||||
int ep = s.indexOf("=");
|
||||
String suffix = s.substring(0,ep);
|
||||
String classname = s.substring(ep+1);
|
||||
if (suffix_rules.get(suffix) != null) {
|
||||
throw new ProblemException("You have already specified a "+
|
||||
"rule for the suffix "+suffix);
|
||||
}
|
||||
if (s.equals(".class")) {
|
||||
throw new ProblemException("You cannot have a translator for .class files!");
|
||||
}
|
||||
if (s.equals(".java")) {
|
||||
throw new ProblemException("You cannot have a translator for .java files!");
|
||||
}
|
||||
String extra = null;
|
||||
int exp = classname.indexOf(",");
|
||||
if (exp != -1) {
|
||||
extra = classname.substring(exp+1);
|
||||
classname = classname.substring(0,exp);
|
||||
}
|
||||
try {
|
||||
Class<?> cl = Class.forName(classname);
|
||||
Transformer t = (Transformer)cl.newInstance();
|
||||
t.setExtra(extra);
|
||||
suffix_rules.put(suffix, t);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new ProblemException("Cannot use "+classname+" as a translator!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the arguments to find the option (-copy) that setup copying rules into the bin dir.
|
||||
* For example: -copy .html
|
||||
* The found copiers are stored as suffix rules as well. No translation is done, just copying.
|
||||
*/
|
||||
private void findCopyOptions(String[] args, Map<String,Transformer> suffix_rules)
|
||||
throws ProblemException, ProblemException {
|
||||
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals("-copy")) {
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a translate rule following -tr.");
|
||||
}
|
||||
String s = args[i+1];
|
||||
checkCopyPattern(s);
|
||||
if (suffix_rules.get(s) != null) {
|
||||
throw new ProblemException("You have already specified a "+
|
||||
"rule for the suffix "+s);
|
||||
}
|
||||
if (s.equals(".class")) {
|
||||
throw new ProblemException("You cannot have a copy rule for .class files!");
|
||||
}
|
||||
if (s.equals(".java")) {
|
||||
throw new ProblemException("You cannot have a copy rule for .java files!");
|
||||
}
|
||||
suffix_rules.put(s, javac_state.getCopier());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite a / separated path into \ separated, but only
|
||||
* if we are running on a platform were File.separatorChar=='\', ie winapi.
|
||||
*/
|
||||
private String fixupSeparator(String p) {
|
||||
if (File.separatorChar == '/') return p;
|
||||
return p.replaceAll("/", "\\\\");
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the arguments for -i -x -xf -if followed by the option
|
||||
* -src, -sourcepath, -modulepath or -classpath and produce a map of all the
|
||||
* files to referenced for that particular option.
|
||||
*
|
||||
* Store the found sources and the found modules in the supplied maps.
|
||||
*/
|
||||
private boolean findFiles(String[] args, String option, Set<String> suffixes,
|
||||
Map<String,Source> found_files, Map<String, Module> found_modules,
|
||||
Module current_module, boolean inLinksrc)
|
||||
throws ProblemException, ProblemException
|
||||
{
|
||||
// Track which source roots, source path roots and class path roots have been added.
|
||||
Set<File> roots = new HashSet<File>();
|
||||
// Track the current set of package includes,excludes as well as excluded source files,
|
||||
// to be used in the next -src/-sourcepath/-classpath
|
||||
List<String> includes = new LinkedList<String>();
|
||||
List<String> excludes = new LinkedList<String>();
|
||||
List<String> excludefiles = new LinkedList<String>();
|
||||
List<String> includefiles = new LinkedList<String>();
|
||||
// This include is used to find all modules in the source.
|
||||
List<String> moduleinfo = new LinkedList<String>();
|
||||
moduleinfo.add("module-info.java");
|
||||
|
||||
for (int i = 0; i<args.length; ++i) {
|
||||
if (args[i].equals("-i")) {
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a package pattern following -i");
|
||||
}
|
||||
String incl = args[i+1];
|
||||
checkPattern(incl);
|
||||
includes.add(incl);
|
||||
}
|
||||
if (args[i].equals("-x")) {
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a package pattern following -x");
|
||||
}
|
||||
String excl = args[i+1];
|
||||
checkPattern(excl);
|
||||
excludes.add(excl);
|
||||
}
|
||||
if (args[i].equals("-xf")) {
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a file following -xf");
|
||||
}
|
||||
String exclf = args[i+1];
|
||||
checkFilePattern(exclf);
|
||||
exclf = Util.normalizeDriveLetter(exclf);
|
||||
excludefiles.add(fixupSeparator(exclf));
|
||||
}
|
||||
if (args[i].equals("-if")) {
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a file following -xf");
|
||||
}
|
||||
String inclf = args[i+1];
|
||||
checkFilePattern(inclf);
|
||||
inclf = Util.normalizeDriveLetter(inclf);
|
||||
includefiles.add(fixupSeparator(inclf));
|
||||
}
|
||||
if (args[i].equals(option)) {
|
||||
if (i+1 >= args.length) {
|
||||
throw new ProblemException("You have to specify a directory following "+option);
|
||||
}
|
||||
String[] root_dirs = args[i+1].split(File.pathSeparator);
|
||||
for (String r : root_dirs) {
|
||||
File root = new File(r);
|
||||
if (!root.isDirectory()) {
|
||||
throw new ProblemException("\""+r+"\" is not a directory.");
|
||||
}
|
||||
try {
|
||||
root = root.getCanonicalFile();
|
||||
} catch (IOException e) {
|
||||
throw new ProblemException(""+e);
|
||||
}
|
||||
if (roots.contains(root)) {
|
||||
throw new ProblemException("\""+r+"\" has already been used for "+option);
|
||||
}
|
||||
if (roots.equals(bin_dir)) {
|
||||
throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -d");
|
||||
}
|
||||
if (roots.equals(gensrc_dir)) {
|
||||
throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -s");
|
||||
}
|
||||
if (roots.equals(header_dir)) {
|
||||
throw new ProblemException("\""+r+"\" cannot be used both for "+option+" and -h");
|
||||
}
|
||||
roots.add(root);
|
||||
Source.scanRoot(root, suffixes, excludes, includes, excludefiles, includefiles,
|
||||
found_files, found_modules, current_module,
|
||||
findBooleanOption(args, "--permit-sources-without-package"),
|
||||
false, inLinksrc);
|
||||
}
|
||||
}
|
||||
if (args[i].equals("-src") ||
|
||||
args[i].equals("-sourcepath") ||
|
||||
args[i].equals("-modulepath") ||
|
||||
args[i].equals("-classpath"))
|
||||
{
|
||||
// Reset the includes,excludes and excludefiles after they have been used.
|
||||
includes = new LinkedList<String>();
|
||||
excludes = new LinkedList<String>();
|
||||
excludefiles = new LinkedList<String>();
|
||||
includefiles = new LinkedList<String>();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
141
langtools/src/share/classes/com/sun/tools/sjavac/Module.java
Normal file
141
langtools/src/share/classes/com/sun/tools/sjavac/Module.java
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The module is the root of a set of packages/sources/artifacts.
|
||||
* At the moment there is only one module in use, the empty/no-name/default module.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class Module implements Comparable<Module> {
|
||||
private String name;
|
||||
private String dirname;
|
||||
private Map<String,Package> packages = new HashMap<String,Package>();
|
||||
private Map<String,Source> sources = new HashMap<String,Source>();
|
||||
private Map<String,File> artifacts = new HashMap<String,File>();
|
||||
|
||||
public Module(String n, String dn) {
|
||||
name = n;
|
||||
dirname = n;
|
||||
}
|
||||
|
||||
public String name() { return name; }
|
||||
public String dirname() { return dirname; }
|
||||
public Map<String,Package> packages() { return packages; }
|
||||
public Map<String,Source> sources() { return sources; }
|
||||
public Map<String,File> artifacts() { return artifacts; }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof Module) && name.equals(((Module)o).name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Module o) {
|
||||
return name.compareTo(o.name);
|
||||
}
|
||||
|
||||
public void save(StringBuilder b) {
|
||||
b.append("M ").append(name).append(":").append("\n");
|
||||
Package.savePackages(packages, b);
|
||||
}
|
||||
|
||||
public static Module load(String l) {
|
||||
int cp = l.indexOf(':',2);
|
||||
if (cp == -1) return null;
|
||||
String name = l.substring(2,cp);
|
||||
return new Module(name, "");
|
||||
}
|
||||
|
||||
public static void saveModules(Map<String,Module> ms, StringBuilder b)
|
||||
{
|
||||
for (Module m : ms.values()) {
|
||||
m.save(b);
|
||||
}
|
||||
}
|
||||
|
||||
public void addPackage(Package p) {
|
||||
packages.put(p.name(), p);
|
||||
}
|
||||
|
||||
public Package lookupPackage(String pkg) {
|
||||
Package p = packages.get(pkg);
|
||||
if (p == null) {
|
||||
p = new Package(this, pkg);
|
||||
packages.put(pkg, p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
public void addSource(String pkg, Source src) {
|
||||
Package p = lookupPackage(pkg);
|
||||
src.setPackage(p);
|
||||
p.addSource(src);
|
||||
sources.put(src.file().getPath(), src);
|
||||
}
|
||||
|
||||
public Source lookupSource(String path) {
|
||||
return sources.get(path);
|
||||
}
|
||||
|
||||
public void addArtifacts(String pkg, Set<URI> as) {
|
||||
Package p = lookupPackage(pkg);
|
||||
for (URI u : as) {
|
||||
p.addArtifact(new File(u));
|
||||
}
|
||||
}
|
||||
|
||||
public void setDependencies(String pkg, Set<String> deps) {
|
||||
Package p = lookupPackage(pkg);
|
||||
p.setDependencies(deps);
|
||||
}
|
||||
|
||||
public void setPubapi(String pkg, List<String> ps) {
|
||||
Package p = lookupPackage(pkg);
|
||||
p.setPubapi(ps);
|
||||
}
|
||||
|
||||
public boolean hasPubapiChanged(String pkg, List<String> ps) {
|
||||
Package p = lookupPackage(pkg);
|
||||
return p.hasPubapiChanged(ps);
|
||||
}
|
||||
}
|
307
langtools/src/share/classes/com/sun/tools/sjavac/Package.java
Normal file
307
langtools/src/share/classes/com/sun/tools/sjavac/Package.java
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 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></p>
|
||||
*/
|
||||
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 depends on these packages.
|
||||
private Set<String> dependencies = new HashSet<String>();
|
||||
// This package has the following dependents, that depend on this package.
|
||||
private Set<String> dependents = new HashSet<String>();
|
||||
// This is the public api of this package.
|
||||
private List<String> pubapi = new ArrayList<String>();
|
||||
// Map from source file name to Source info object.
|
||||
private Map<String,Source> sources = new HashMap<String,Source>();
|
||||
// This package generated these artifacts.
|
||||
private Map<String,File> artifacts = new HashMap<String,File>();
|
||||
|
||||
public Package(Module m, String n) {
|
||||
int c = n.indexOf(":");
|
||||
assert(c != -1);
|
||||
String mn = n.substring(0,c);
|
||||
assert(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 List<String> pubapi() { return pubapi; }
|
||||
|
||||
public Set<String> dependencies() { return dependencies; }
|
||||
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);
|
||||
}
|
||||
|
||||
public void addDependency(String d) {
|
||||
dependencies.add(d);
|
||||
}
|
||||
|
||||
public void addDependent(String d) {
|
||||
dependents.add(d);
|
||||
}
|
||||
|
||||
public void addPubapi(String p) {
|
||||
pubapi.add(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.size() > 0;
|
||||
}
|
||||
|
||||
public static List<String> pubapiToList(String ps)
|
||||
{
|
||||
String[] lines = ps.split("\n");
|
||||
List<String> r = new ArrayList<String>();
|
||||
for (String l : lines) {
|
||||
r.add(l);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public boolean hasPubapiChanged(List<String> ps) {
|
||||
Iterator<String> i = ps.iterator();
|
||||
Iterator<String> j = pubapi.iterator();
|
||||
int line = 0;
|
||||
while (i.hasNext() && j.hasNext()) {
|
||||
String is = i.next();
|
||||
String js = j.next();
|
||||
if (!is.equals(js)) {
|
||||
Log.debug("Change in pubapi for package "+name+" line "+line);
|
||||
Log.debug("Old: "+js);
|
||||
Log.debug("New: "+is);
|
||||
return true;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
if ((i.hasNext() && !j.hasNext() ) ||
|
||||
(!i.hasNext() && j.hasNext())) {
|
||||
Log.debug("Change in pubapi for package "+name);
|
||||
if (i.hasNext()) {
|
||||
Log.debug("New has more lines!");
|
||||
} else {
|
||||
Log.debug("Old has more lines!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setPubapi(List<String> ps) {
|
||||
pubapi = ps;
|
||||
}
|
||||
|
||||
public void setDependencies(Set<String> ds) {
|
||||
dependencies = ds;
|
||||
}
|
||||
|
||||
public void save(StringBuilder b) {
|
||||
b.append("P ").append(name).append("\n");
|
||||
Source.saveSources(sources, b);
|
||||
saveDependencies(b);
|
||||
savePubapi(b);
|
||||
saveArtifacts(b);
|
||||
}
|
||||
|
||||
static public Package load(Module module, String l) {
|
||||
String name = l.substring(2);
|
||||
return new Package(module, name);
|
||||
}
|
||||
|
||||
public void loadDependency(String l) {
|
||||
String n = l.substring(2);
|
||||
addDependency(n);
|
||||
}
|
||||
|
||||
public void loadPubapi(String l) {
|
||||
String pi = l.substring(2);
|
||||
addPubapi(pi);
|
||||
}
|
||||
|
||||
public void saveDependencies(StringBuilder b) {
|
||||
List<String> sorted_dependencies = new ArrayList<String>();
|
||||
for (String key : dependencies) {
|
||||
sorted_dependencies.add(key);
|
||||
}
|
||||
Collections.sort(sorted_dependencies);
|
||||
for (String a : sorted_dependencies) {
|
||||
b.append("D "+a+"\n");
|
||||
}
|
||||
}
|
||||
|
||||
public void savePubapi(StringBuilder b) {
|
||||
for (String l : pubapi) {
|
||||
b.append("I "+l+"\n");
|
||||
}
|
||||
}
|
||||
|
||||
public static void savePackages(Map<String,Package> packages, StringBuilder b) {
|
||||
List<String> sorted_packages = new ArrayList<String>();
|
||||
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(!artifacts.isEmpty());
|
||||
artifacts = new HashMap<String,File>();
|
||||
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<File>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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;
|
||||
|
||||
/**
|
||||
* Used to signal serious problems when running sjavac.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class ProblemException extends Exception {
|
||||
static final long serialVersionUID = -3387516993124229949L;
|
||||
public ProblemException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
400
langtools/src/share/classes/com/sun/tools/sjavac/Source.java
Normal file
400
langtools/src/share/classes/com/sun/tools/sjavac/Source.java
Normal file
@ -0,0 +1,400 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.util.Set;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
/** A Source object maintains information about a source file.
|
||||
* For example which package it belongs to and kind of source it is.
|
||||
* The class also knows how to find source files (scanRoot) given include/exclude
|
||||
* patterns and a root.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class Source implements Comparable<Source> {
|
||||
// The package the source belongs to.
|
||||
private Package pkg;
|
||||
// Name of this source file, relative its source root.
|
||||
// For example: java/lang/Object.java
|
||||
// Or if the source file is inside a module:
|
||||
// jdk.base/java/lang/Object.java
|
||||
private String name;
|
||||
// What kind of file is this.
|
||||
private String suffix;
|
||||
// When this source file was last_modified
|
||||
private long lastModified;
|
||||
// The source File.
|
||||
private File file;
|
||||
// The source root under which file resides.
|
||||
private File root;
|
||||
// If the source is generated.
|
||||
private boolean isGenerated;
|
||||
// If the source is only linked to, not compiled.
|
||||
private boolean linkedOnly;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof Source) && name.equals(((Source)o).name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Source o) {
|
||||
return name.compareTo(o.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
public Source(Module m, String n, File f, File r) {
|
||||
name = n;
|
||||
int dp = n.lastIndexOf(".");
|
||||
if (dp != -1) {
|
||||
suffix = n.substring(dp);
|
||||
} else {
|
||||
suffix = "";
|
||||
}
|
||||
file = f;
|
||||
root = r;
|
||||
lastModified = f.lastModified();
|
||||
linkedOnly = false;
|
||||
}
|
||||
|
||||
public Source(Package p, String n, long lm) {
|
||||
pkg = p;
|
||||
name = n;
|
||||
int dp = n.lastIndexOf(".");
|
||||
if (dp != -1) {
|
||||
suffix = n.substring(dp);
|
||||
} else {
|
||||
suffix = "";
|
||||
}
|
||||
file = null;
|
||||
root = null;
|
||||
lastModified = lm;
|
||||
linkedOnly = false;
|
||||
int ls = n.lastIndexOf('/');
|
||||
}
|
||||
|
||||
public String name() { return name; }
|
||||
public String suffix() { return suffix; }
|
||||
public Package pkg() { return pkg; }
|
||||
public File file() { return file; }
|
||||
public File root() { return root; }
|
||||
public long lastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
public void setPackage(Package p) {
|
||||
pkg = p;
|
||||
}
|
||||
|
||||
public void markAsGenerated() {
|
||||
isGenerated = true;
|
||||
}
|
||||
|
||||
public boolean isGenerated() {
|
||||
return isGenerated;
|
||||
}
|
||||
|
||||
public void markAsLinkedOnly() {
|
||||
linkedOnly = true;
|
||||
}
|
||||
|
||||
public boolean isLinkedOnly() {
|
||||
return linkedOnly;
|
||||
}
|
||||
|
||||
private void save(StringBuilder b) {
|
||||
String CL = linkedOnly?"L":"C";
|
||||
String GS = isGenerated?"G":"S";
|
||||
b.append(GS+" "+CL+" "+name+" "+file.lastModified()+"\n");
|
||||
}
|
||||
// Parse a line that looks like this:
|
||||
// S C /code/alfa/A.java 1357631228000
|
||||
static public Source load(Package lastPackage, String l, boolean isGenerated) {
|
||||
int sp = l.indexOf(' ',4);
|
||||
if (sp == -1) return null;
|
||||
String name = l.substring(4,sp);
|
||||
long last_modified = Long.parseLong(l.substring(sp+1));
|
||||
|
||||
boolean isLinkedOnly = false;
|
||||
if (l.charAt(2) == 'L') {
|
||||
isLinkedOnly = true;
|
||||
} else if (l.charAt(2) == 'C') {
|
||||
isLinkedOnly = false;
|
||||
} else return null;
|
||||
|
||||
Source s = new Source(lastPackage, name, last_modified);
|
||||
s.file = new File(name);
|
||||
if (isGenerated) s.markAsGenerated();
|
||||
if (isLinkedOnly) s.markAsLinkedOnly();
|
||||
return s;
|
||||
}
|
||||
|
||||
public static void saveSources(Map<String,Source> sources, StringBuilder b) {
|
||||
List<String> sorted_sources = new ArrayList<String>();
|
||||
for (String key : sources.keySet()) {
|
||||
sorted_sources.add(key);
|
||||
}
|
||||
Collections.sort(sorted_sources);
|
||||
for (String key : sorted_sources) {
|
||||
Source s = sources.get(key);
|
||||
s.save(b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recurse into the directory root and find all files matchine the excl/incl/exclfiles/inclfiles rules.
|
||||
* Detects the existence of module-info.java files and presumes that the directory it resides in
|
||||
* is the name of the current module.
|
||||
*/
|
||||
static public void scanRoot(File root,
|
||||
Set<String> suffixes,
|
||||
List<String> excludes, List<String> includes,
|
||||
List<String> excludeFiles, List<String> includeFiles,
|
||||
Map<String,Source> foundFiles,
|
||||
Map<String,Module> foundModules,
|
||||
Module currentModule,
|
||||
boolean permitSourcesWithoutPackage,
|
||||
boolean inGensrc,
|
||||
boolean inLinksrc)
|
||||
throws ProblemException {
|
||||
|
||||
if (root == null) return;
|
||||
int root_prefix = root.getPath().length()+1;
|
||||
// This is the root source directory, it must not contain any Java sources files
|
||||
// because we do not allow Java source files without a package.
|
||||
// (Unless of course --permit-sources-without-package has been specified.)
|
||||
// It might contain other source files however, (for -tr and -copy) these will
|
||||
// always be included, since no package pattern can match the root directory.
|
||||
currentModule = addFilesInDir(root, root_prefix, root, suffixes, permitSourcesWithoutPackage,
|
||||
excludeFiles, includeFiles, false,
|
||||
foundFiles, foundModules, currentModule,
|
||||
inGensrc, inLinksrc);
|
||||
|
||||
File[] dirfiles = root.listFiles();
|
||||
for (File d : dirfiles) {
|
||||
if (d.isDirectory()) {
|
||||
// Descend into the directory structure.
|
||||
scanDirectory(d, root_prefix, root, suffixes,
|
||||
excludes, includes, excludeFiles, includeFiles,
|
||||
false, foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a path matches any of the patterns given.
|
||||
* The pattern foo.bar matches only foo.bar
|
||||
* The pattern foo.* matches foo.bar and foo.bar.zoo etc
|
||||
*/
|
||||
static private boolean hasMatch(String path, List<String> patterns) {
|
||||
for (String p : patterns) {
|
||||
// Exact match
|
||||
if (p.equals(path)) {
|
||||
return true;
|
||||
}
|
||||
// Single dot the end matches this package and all its subpackages.
|
||||
if (p.endsWith(".*")) {
|
||||
// Remove the wildcard
|
||||
String patprefix = p.substring(0,p.length()-2);
|
||||
// Does the path start with the pattern prefix?
|
||||
if (path.startsWith(patprefix)) {
|
||||
// If the path has the same length as the pattern prefix, then it is a match.
|
||||
// If the path is longer, then make sure that
|
||||
// the next part of the path starts with a dot (.) to prevent
|
||||
// wildcard matching in the middle of a package name.
|
||||
if (path.length()==patprefix.length() || path.charAt(patprefix.length())=='.') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches patterns with the asterisk first. */
|
||||
// The pattern foo/bar.java only matches foo/bar.java
|
||||
// The pattern */bar.java matches foo/bar.java and zoo/bar.java etc
|
||||
static private boolean hasFileMatch(String path, List<String> patterns) {
|
||||
path = Util.normalizeDriveLetter(path);
|
||||
for (String p : patterns) {
|
||||
// Exact match
|
||||
if (p.equals(path)) {
|
||||
return true;
|
||||
}
|
||||
// Single dot the end matches this package and all its subpackages.
|
||||
if (p.startsWith("*")) {
|
||||
// Remove the wildcard
|
||||
String patsuffix = p.substring(1);
|
||||
// Does the path start with the pattern prefix?
|
||||
if (path.endsWith(patsuffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the files in the directory, assuming that the file has not been excluded.
|
||||
* Returns a fresh Module object, if this was a dir with a module-info.java file.
|
||||
*/
|
||||
static private Module addFilesInDir(File dir, int rootPrefix, File root,
|
||||
Set<String> suffixes, boolean allow_javas,
|
||||
List<String> excludeFiles, List<String> includeFiles, boolean all,
|
||||
Map<String,Source> foundFiles,
|
||||
Map<String,Module> foundModules,
|
||||
Module currentModule,
|
||||
boolean inGensrc,
|
||||
boolean inLinksrc)
|
||||
throws ProblemException
|
||||
{
|
||||
for (File f : dir.listFiles()) {
|
||||
if (f.isFile()) {
|
||||
boolean should_add =
|
||||
(excludeFiles == null || excludeFiles.isEmpty() || !hasFileMatch(f.getPath(), excludeFiles))
|
||||
&& (includeFiles == null || includeFiles.isEmpty() || hasFileMatch(f.getPath(), includeFiles));
|
||||
|
||||
if (should_add) {
|
||||
if (!allow_javas && f.getName().endsWith(".java")) {
|
||||
throw new ProblemException("No .java files are allowed in the source root "+dir.getPath()+
|
||||
", please remove "+f.getName());
|
||||
}
|
||||
// Extract the file name relative the root.
|
||||
String fn = f.getPath().substring(rootPrefix);
|
||||
// Extract the package name.
|
||||
int sp = fn.lastIndexOf(File.separatorChar);
|
||||
String pkg = "";
|
||||
if (sp != -1) {
|
||||
pkg = fn.substring(0,sp).replace(File.separatorChar,'.');
|
||||
}
|
||||
// Is this a module-info.java file?
|
||||
if (fn.endsWith("module-info.java")) {
|
||||
// Aha! We have recursed into a module!
|
||||
if (!currentModule.name().equals("")) {
|
||||
throw new ProblemException("You have an extra module-info.java inside a module! Please remove "+fn);
|
||||
}
|
||||
String module_name = fn.substring(0,fn.length()-16);
|
||||
currentModule = new Module(module_name, f.getPath());
|
||||
foundModules.put(module_name, currentModule);
|
||||
}
|
||||
// Extract the suffix.
|
||||
int dp = fn.lastIndexOf(".");
|
||||
String suffix = "";
|
||||
if (dp > 0) {
|
||||
suffix = fn.substring(dp);
|
||||
}
|
||||
// Should the file be added?
|
||||
if (all || suffixes.contains(suffix)) {
|
||||
Source of = foundFiles.get(f.getPath());
|
||||
if (of != null) {
|
||||
throw new ProblemException("You have already added the file "+fn+" from "+of.file().getPath());
|
||||
}
|
||||
of = currentModule.lookupSource(f.getPath());
|
||||
if (of != null) {
|
||||
// Oups, the source is already added, could be ok, could be not, lets check.
|
||||
if (inLinksrc) {
|
||||
// So we are collecting sources for linking only.
|
||||
if (of.isLinkedOnly()) {
|
||||
// Ouch, this one is also for linking only. Bad.
|
||||
throw new ProblemException("You have already added the link only file "+fn+" from "+of.file().getPath());
|
||||
}
|
||||
// Ok, the existing source is to be compiled. Thus this link only is redundant
|
||||
// since all compiled are also linked to. Continue to the next source.
|
||||
// But we need to add the source, so that it will be visible to linking,
|
||||
// if not the multi core compile will fail because a JavaCompiler cannot
|
||||
// find the necessary dependencies for its part of the source.
|
||||
foundFiles.put(f.getPath(), of);
|
||||
continue;
|
||||
} else {
|
||||
// We are looking for sources to compile, if we find an existing to be compiled
|
||||
// source with the same name, it is an internal error, since we must
|
||||
// find the sources to be compiled before we find the sources to be linked to.
|
||||
throw new ProblemException("Internal error: Double add of file "+fn+" from "+of.file().getPath());
|
||||
}
|
||||
}
|
||||
Source s = new Source(currentModule, f.getPath(), f, root);
|
||||
if (inGensrc) s.markAsGenerated();
|
||||
if (inLinksrc) {
|
||||
s.markAsLinkedOnly();
|
||||
}
|
||||
pkg = currentModule.name()+":"+pkg;
|
||||
foundFiles.put(f.getPath(), s);
|
||||
currentModule.addSource(pkg, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentModule;
|
||||
}
|
||||
|
||||
private static boolean gurka = false;
|
||||
|
||||
static private void scanDirectory(File dir, int rootPrefix, File root,
|
||||
Set<String> suffixes,
|
||||
List<String> excludes, List<String> includes,
|
||||
List<String> excludeFiles, List<String> includeFiles, boolean all,
|
||||
Map<String,Source> foundFiles,
|
||||
Map<String,Module> foundModules,
|
||||
Module currentModule, boolean inGensrc, boolean inLinksrc)
|
||||
throws ProblemException {
|
||||
|
||||
String pkg_name = "";
|
||||
// Remove the root prefix from the dir path, and replace file separator with dots
|
||||
// to get the package name.
|
||||
if (dir.getPath().length() > rootPrefix) {
|
||||
pkg_name = dir.getPath().substring(rootPrefix).replace(File.separatorChar,'.');
|
||||
}
|
||||
// Should this package directory be included and not excluded?
|
||||
if (all || ((includes==null || includes.isEmpty() || hasMatch(pkg_name, includes)) &&
|
||||
(excludes==null || excludes.isEmpty() || !hasMatch(pkg_name, excludes)))) {
|
||||
// Add the source files.
|
||||
currentModule = addFilesInDir(dir, rootPrefix, root, suffixes, true, excludeFiles, includeFiles, all,
|
||||
foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
|
||||
}
|
||||
|
||||
for (File d : dir.listFiles()) {
|
||||
if (d.isDirectory()) {
|
||||
// Descend into the directory structure.
|
||||
scanDirectory(d, rootPrefix, root, suffixes,
|
||||
excludes, includes, excludeFiles, includeFiles, all,
|
||||
foundFiles, foundModules, currentModule, inGensrc, inLinksrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.PrintStream;
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The transform interface is used to transform content inside a package, from one form to another.
|
||||
* Usually the output form is an unpredictable number of output files. (eg class files)
|
||||
* but can also be an unpredictable number of generated source files (eg idl2java)
|
||||
* or a single predictable output file (eg when copying,cleaning or compiling a properties file).
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public interface Transformer
|
||||
{
|
||||
/**
|
||||
* The transform method takes a set of package names, mapped to their source files and to the
|
||||
* pubapis of the packages.
|
||||
*
|
||||
* The transform implementation must:
|
||||
* store the names of the generated artifacts for each package into package_artifacts
|
||||
* store found dependencies to other packages into the supplied set package_dependencies
|
||||
* store the public api for a package into the supplied set package_pubapis
|
||||
*
|
||||
* Any benign messages as a result of running the transform
|
||||
* are written into stdout, and errors are written to stderr.
|
||||
*
|
||||
* The debug_level can be 0=silent (only warnings and errors) 1=normal 2=verbose 3 or greater=debug
|
||||
* setExtra is used to set the extra information information that can be passed on
|
||||
* the command line to the smart javac wrapper.
|
||||
*
|
||||
* If sjavac is building incrementally from an existing javac_state, the var incremental is true.
|
||||
*
|
||||
* The transformer will only be called if some source in the package (or dependency) has
|
||||
* a modified timestamp. Thus the transformer might get called with many sources, of which
|
||||
* only one has changed. The transformer is allowed to regenerate all artifacts but
|
||||
* a better transformer will only write those artifacts that need updating.
|
||||
*
|
||||
* However the transformer must verify that the existing artifacts really are there!
|
||||
* and it must always update package_artifacts, package_dependencies, and package_pubapis correctly.
|
||||
* This means that at least for Java source, it will always have to recompile the sources.
|
||||
*
|
||||
* The transformer is allowed to put files anywhere in the dest_root.
|
||||
* An example of this is, can be the META-INF transformer that copy files
|
||||
* below META-INF directories to the single META-INF directory below dest_root.
|
||||
*
|
||||
* False is returned if there was an error that prevented the transform.
|
||||
* I.e. something was printed on stderr.
|
||||
*
|
||||
* If num_cores is set to a non-zero value. The transform should attempt to use no more than these
|
||||
* number of threads for heavy work.
|
||||
*/
|
||||
boolean transform(Map<String,Set<URI>> pkgSrcs,
|
||||
Set<URI> visibleSources,
|
||||
Map<URI,Set<String>> visibleClasses,
|
||||
Map<String,Set<String>> oldPackageDependencies,
|
||||
URI destRoot,
|
||||
Map<String,Set<URI>> packageArtifacts,
|
||||
Map<String,Set<String>> packageDependencies,
|
||||
Map<String,String> packagePublicApis,
|
||||
int debugLevel,
|
||||
boolean incremental,
|
||||
int numCores,
|
||||
PrintStream out,
|
||||
PrintStream err);
|
||||
|
||||
void setExtra(String e);
|
||||
void setExtra(String[] args);
|
||||
}
|
160
langtools/src/share/classes/com/sun/tools/sjavac/Util.java
Normal file
160
langtools/src/share/classes/com/sun/tools/sjavac/Util.java
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Utilities.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class Util {
|
||||
|
||||
public static String toFileSystemPath(String pkgId) {
|
||||
if (pkgId == null || pkgId.length()==0) return null;
|
||||
String pn;
|
||||
if (pkgId.charAt(0) == ':') {
|
||||
// When the module is the default empty module.
|
||||
// Do not prepend the module directory, because there is none.
|
||||
// Thus :java.foo.bar translates to java/foo/bar (or \)
|
||||
pn = pkgId.substring(1).replace('.',File.separatorChar);
|
||||
} else {
|
||||
// There is a module. Thus jdk.base:java.foo.bar translates
|
||||
// into jdk.base/java/foo/bar
|
||||
int cp = pkgId.indexOf(':');
|
||||
String mn = pkgId.substring(0,cp);
|
||||
pn = mn+File.separatorChar+pkgId.substring(cp+1).replace('.',File.separatorChar);
|
||||
}
|
||||
return pn;
|
||||
}
|
||||
|
||||
public static String justPackageName(String pkgName) {
|
||||
int c = pkgName.indexOf(":");
|
||||
assert(c != -1);
|
||||
return pkgName.substring(c+1);
|
||||
}
|
||||
|
||||
public static String extractStringOption(String opName, String s) {
|
||||
int p = s.indexOf(opName+"=");
|
||||
if (p == -1) return null;
|
||||
p+=opName.length()+1;
|
||||
int pe = s.indexOf(',', p);
|
||||
if (pe == -1) pe = s.length();
|
||||
return s.substring(p, pe);
|
||||
}
|
||||
|
||||
public static int extractIntOption(String opName, String s) {
|
||||
int p = s.indexOf(opName+"=");
|
||||
if (p == -1) return 0;
|
||||
p+=opName.length()+1;
|
||||
int pe = s.indexOf(',', p);
|
||||
if (pe == -1) pe = s.length();
|
||||
int v = 0;
|
||||
try {
|
||||
v = Integer.parseInt(s.substring(p, pe));
|
||||
} catch (Exception e) {}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean out unwanted sub options supplied inside a primary option.
|
||||
* For example to only had portfile remaining from:
|
||||
* settings="--server:id=foo,portfile=bar"
|
||||
* do settings = cleanOptions("--server:",Util.set("-portfile"),settings);
|
||||
* now settings equals "--server:portfile=bar"
|
||||
*
|
||||
* @param optionPrefix The option name, including colon, eg --server:
|
||||
* @param allowsSubOptions A set of the allowed sub options, id portfile etc.
|
||||
* @param s The option settings string.
|
||||
*/
|
||||
public static String cleanSubOptions(String optionPrefix, Set<String> allowedSubOptions, String s) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!s.startsWith(optionPrefix)) return "";
|
||||
StringTokenizer st = new StringTokenizer(s.substring(optionPrefix.length()), ",");
|
||||
while (st.hasMoreTokens()) {
|
||||
String o = st.nextToken();
|
||||
int p = o.indexOf('=');
|
||||
if (p>0) {
|
||||
String key = o.substring(0,p);
|
||||
String val = o.substring(p+1);
|
||||
if (allowedSubOptions.contains(key)) {
|
||||
if (sb.length() > 0) sb.append(',');
|
||||
sb.append(key+"="+val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to create a set with strings.
|
||||
*/
|
||||
public static Set<String> set(String... ss) {
|
||||
Set<String> set = new HashSet<String>();
|
||||
set.addAll(Arrays.asList(ss));
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize windows drive letter paths to upper case to enable string
|
||||
* comparison.
|
||||
*
|
||||
* @param file File name to normalize
|
||||
* @return The normalized string if file has a drive letter at the beginning,
|
||||
* otherwise the original string.
|
||||
*/
|
||||
public static String normalizeDriveLetter(String file) {
|
||||
if (file.length() > 2 && file.charAt(1) == ':') {
|
||||
return Character.toUpperCase(file.charAt(0)) + file.substring(1);
|
||||
} else if (file.length() > 3 && file.charAt(0) == '*'
|
||||
&& file.charAt(2) == ':') {
|
||||
// Handle a wildcard * at the beginning of the string.
|
||||
return file.substring(0, 1) + Character.toUpperCase(file.charAt(1))
|
||||
+ file.substring(2);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the setting for the server properties.
|
||||
*/
|
||||
public static String findServerSettings(String[] args) {
|
||||
for (String s : args) {
|
||||
if (s.startsWith("--server:")) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2011, 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.comp;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.Log;
|
||||
import com.sun.tools.javac.util.Name;
|
||||
|
||||
/** Utility class containing dependency information between packages
|
||||
* and the pubapi for a package.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class Dependencies {
|
||||
protected static final Context.Key<Dependencies> dependenciesKey =
|
||||
new Context.Key<Dependencies>();
|
||||
|
||||
// The log to be used for error reporting.
|
||||
protected Log log;
|
||||
// Map from package name to packages that the package depends upon.
|
||||
protected Map<Name,Set<Name>> deps;
|
||||
// This is the set of all packages that are supplied
|
||||
// through the java files at the command line.
|
||||
protected Set<Name> explicitPackages;
|
||||
|
||||
// Map from a package name to its public api.
|
||||
// Will the Name encode the module in the future?
|
||||
// If not, this will have to change to map from Module+Name to public api.
|
||||
protected Map<Name,StringBuffer> publicApiPerClass;
|
||||
|
||||
public static Dependencies instance(Context context) {
|
||||
Dependencies instance = context.get(dependenciesKey);
|
||||
if (instance == null)
|
||||
instance = new Dependencies(context);
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Dependencies(Context context) {
|
||||
context.put(dependenciesKey, this);
|
||||
log = Log.instance(context);
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
deps = new HashMap<Name, Set<Name>>();
|
||||
explicitPackages = new HashSet<Name>();
|
||||
publicApiPerClass = new HashMap<Name,StringBuffer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the set of dependencies that are relevant to the compile
|
||||
* that has just been performed. I.e. we are only interested in
|
||||
* dependencies for classes that were explicitly compiled.
|
||||
* @return
|
||||
*/
|
||||
public Map<String,Set<String>> getDependencies() {
|
||||
Map<String,Set<String>> new_deps = new HashMap<String,Set<String>>();
|
||||
if (explicitPackages == null) return new_deps;
|
||||
for (Name pkg : explicitPackages) {
|
||||
Set<Name> set = deps.get(pkg);
|
||||
if (set != null) {
|
||||
Set<String> new_set = new_deps.get(pkg.toString());
|
||||
if (new_set == null) {
|
||||
new_set = new HashSet<String>();
|
||||
// Modules beware....
|
||||
new_deps.put(":"+pkg.toString(), new_set);
|
||||
}
|
||||
for (Name d : set) {
|
||||
new_set.add(":"+d.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new_deps;
|
||||
}
|
||||
|
||||
class CompareNames implements Comparator<Name> {
|
||||
public int compare(Name a, Name b) {
|
||||
return a.toString().compareTo(b.toString());
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return super.equals(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the map from class names to their pubapi to a map
|
||||
* from package names to their pubapi (which is the sorted concatenation
|
||||
* of all the class pubapis)
|
||||
*/
|
||||
public Map<String,String> getPubapis() {
|
||||
Map<String,String> publicApiPerPackage = new HashMap<String,String>();
|
||||
if (publicApiPerClass == null) return publicApiPerPackage;
|
||||
Name[] keys = publicApiPerClass.keySet().toArray(new Name[0]);
|
||||
Arrays.sort(keys, new CompareNames());
|
||||
StringBuffer newPublicApi = new StringBuffer();
|
||||
int i=0;
|
||||
String prevPkg = "";
|
||||
for (Name k : keys) {
|
||||
String cn = k.toString();
|
||||
String pn = "";
|
||||
int dp = cn.lastIndexOf('.');
|
||||
if (dp != -1) {
|
||||
pn = cn.substring(0,dp);
|
||||
}
|
||||
if (!pn.equals(prevPkg)) {
|
||||
if (!prevPkg.equals("")) {
|
||||
// Add default module name ":"
|
||||
publicApiPerPackage.put(":"+prevPkg, newPublicApi.toString());
|
||||
}
|
||||
newPublicApi = new StringBuffer();
|
||||
prevPkg = pn;
|
||||
}
|
||||
newPublicApi.append(publicApiPerClass.get(k));
|
||||
i++;
|
||||
}
|
||||
if (!prevPkg.equals(""))
|
||||
publicApiPerPackage.put(":"+prevPkg, newPublicApi.toString());
|
||||
return publicApiPerPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit the api of a class and construct a pubapi string and
|
||||
* store it into the pubapi_perclass map.
|
||||
*/
|
||||
public void visitPubapi(Element e) {
|
||||
Name n = ((ClassSymbol)e).fullname;
|
||||
Name p = ((ClassSymbol)e).packge().fullname;
|
||||
StringBuffer sb = publicApiPerClass.get(n);
|
||||
assert(sb == null);
|
||||
sb = new StringBuffer();
|
||||
PubapiVisitor v = new PubapiVisitor(sb);
|
||||
v.visit(e);
|
||||
if (sb.length()>0) {
|
||||
publicApiPerClass.put(n, sb);
|
||||
}
|
||||
explicitPackages.add(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect a dependency. curr_pkg is marked as depending on dep_pkg.
|
||||
*/
|
||||
public void collect(Name currPkg, Name depPkg) {
|
||||
if (!currPkg.equals(depPkg)) {
|
||||
Set<Name> theset = deps.get(currPkg);
|
||||
if (theset==null) {
|
||||
theset = new HashSet<Name>();
|
||||
deps.put(currPkg, theset);
|
||||
}
|
||||
theset.add(depPkg);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.comp;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import com.sun.tools.javac.main.JavaCompiler;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||
import com.sun.tools.sjavac.server.CompilerThread;
|
||||
import java.io.File;
|
||||
|
||||
/** Subclass to Resolve that overrides collect.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class JavaCompilerWithDeps extends JavaCompiler {
|
||||
|
||||
/** The dependency database
|
||||
*/
|
||||
protected Dependencies deps;
|
||||
protected CompilerThread compilerThread;
|
||||
|
||||
public JavaCompilerWithDeps(Context context, CompilerThread t) {
|
||||
super(context);
|
||||
deps = Dependencies.instance(context);
|
||||
compilerThread = t;
|
||||
needRootClasses = true;
|
||||
}
|
||||
|
||||
public static void preRegister(Context context, final CompilerThread t) {
|
||||
context.put(compilerKey, new Context.Factory<JavaCompiler>() {
|
||||
public JavaCompiler make(Context c) {
|
||||
JavaCompiler instance = new JavaCompilerWithDeps(c, t);
|
||||
c.put(JavaCompiler.class, instance);
|
||||
return instance;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Collect the public apis of classes supplied explicitly for compilation.
|
||||
* @param sym The class to visit.
|
||||
*/
|
||||
@Override
|
||||
public void reportPublicApi(ClassSymbol sym) {
|
||||
// The next test will catch when source files are located in the wrong directory!
|
||||
// This ought to be moved into javac as a new warning, or perhaps as part
|
||||
// of the auxiliary class warning.
|
||||
|
||||
// For example if sun.swing.BeanInfoUtils
|
||||
// is in fact stored in: /mybuild/jdk/gensrc/javax/swing/beaninfo/BeanInfoUtils.java
|
||||
|
||||
// We do not need to test that BeanInfoUtils is stored in a file named BeanInfoUtils
|
||||
// since this is checked earlier.
|
||||
if (sym.sourcefile != null) {
|
||||
// Rewrite sun.swing.BeanInfoUtils into /sun/swing/
|
||||
StringBuilder pathb = new StringBuilder();
|
||||
StringTokenizer qn = new StringTokenizer(sym.packge().toString(), ".");
|
||||
boolean first = true;
|
||||
while (qn.hasMoreTokens()) {
|
||||
String o = qn.nextToken();
|
||||
pathb.append("/");
|
||||
pathb.append(o);
|
||||
first = false;
|
||||
}
|
||||
pathb.append("/");
|
||||
String path = pathb.toString();
|
||||
|
||||
// Now cut the uri to be: file:///mybuild/jdk/gensrc/javax/swing/beaninfo/
|
||||
String p = sym.sourcefile.toUri().getPath();
|
||||
// Do not use File.separatorChar here, a URI always uses slashes /.
|
||||
int i = p.lastIndexOf("/");
|
||||
String pp = p.substring(0,i+1);
|
||||
|
||||
// Now check if the truncated uri ends with the path. (It does not == failure!)
|
||||
if (path.length() > 0 && !path.equals("/unnamed package/") && !pp.endsWith(path)) {
|
||||
compilerThread.logError("Error: The source file "+sym.sourcefile.getName()+
|
||||
" is located in the wrong package directory, because it contains the class "+
|
||||
sym.getQualifiedName());
|
||||
}
|
||||
}
|
||||
deps.visitPubapi(sym);
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.comp;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.ElementScanner6;
|
||||
|
||||
/** Utility class that constructs a textual representation
|
||||
* of the public api of a class.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class PubapiVisitor extends ElementScanner6<Void, Void> {
|
||||
|
||||
StringBuffer sb;
|
||||
// Important that it is 1! Part of protocol over wire, silly yes.
|
||||
// Fix please.
|
||||
int indent = 1;
|
||||
|
||||
public PubapiVisitor(StringBuffer sb) {
|
||||
this.sb = sb;
|
||||
}
|
||||
|
||||
String depth(int l) {
|
||||
return " ".substring(0, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitType(TypeElement e, Void p) {
|
||||
if (e.getModifiers().contains(Modifier.PUBLIC)
|
||||
|| e.getModifiers().contains(Modifier.PROTECTED))
|
||||
{
|
||||
sb.append(depth(indent) + "TYPE " + e.getQualifiedName() + "\n");
|
||||
indent += 2;
|
||||
Void v = super.visitType(e, p);
|
||||
indent -= 2;
|
||||
return v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitVariable(VariableElement e, Void p) {
|
||||
if (e.getModifiers().contains(Modifier.PUBLIC)
|
||||
|| e.getModifiers().contains(Modifier.PROTECTED)) {
|
||||
sb.append(depth(indent)).append("VAR ")
|
||||
.append(makeVariableString(e)).append("\n");
|
||||
}
|
||||
// Safe to not recurse here, because the only thing
|
||||
// to visit here is the constructor of a variable declaration.
|
||||
// If it happens to contain an anonymous inner class (which it might)
|
||||
// then this class is never visible outside of the package anyway, so
|
||||
// we are allowed to ignore it here.
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitExecutable(ExecutableElement e, Void p) {
|
||||
if (e.getModifiers().contains(Modifier.PUBLIC)
|
||||
|| e.getModifiers().contains(Modifier.PROTECTED)) {
|
||||
sb.append(depth(indent)).append("METHOD ")
|
||||
.append(makeMethodString(e)).append("\n");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a String representation of a method element with everything
|
||||
* necessary to track all public aspects of it in an API.
|
||||
* @param e Element to create String for.
|
||||
* @return String representation of element.
|
||||
*/
|
||||
protected String makeMethodString(ExecutableElement e) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (Modifier modifier : e.getModifiers()) {
|
||||
result.append(modifier.toString());
|
||||
result.append(" ");
|
||||
}
|
||||
result.append(e.getReturnType().toString());
|
||||
result.append(" ");
|
||||
result.append(e.toString());
|
||||
|
||||
List<? extends TypeMirror> thrownTypes = e.getThrownTypes();
|
||||
if (!thrownTypes.isEmpty()) {
|
||||
result.append(" throws ");
|
||||
for (Iterator<? extends TypeMirror> iterator = thrownTypes
|
||||
.iterator(); iterator.hasNext();) {
|
||||
TypeMirror typeMirror = iterator.next();
|
||||
result.append(typeMirror.toString());
|
||||
if (iterator.hasNext()) {
|
||||
result.append(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a String representation of a variable element with everything
|
||||
* necessary to track all public aspects of it in an API.
|
||||
* @param e Element to create String for.
|
||||
* @return String representation of element.
|
||||
*/
|
||||
protected String makeVariableString(VariableElement e) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (Modifier modifier : e.getModifiers()) {
|
||||
result.append(modifier.toString());
|
||||
result.append(" ");
|
||||
}
|
||||
result.append(e.asType().toString());
|
||||
result.append(" ");
|
||||
result.append(e.toString());
|
||||
Object value = e.getConstantValue();
|
||||
if (value != null) {
|
||||
result.append(" = ");
|
||||
if (e.asType().toString().equals("char")) {
|
||||
int v = (int)value.toString().charAt(0);
|
||||
result.append("'\\u"+Integer.toString(v,16)+"'");
|
||||
} else {
|
||||
result.append(value.toString());
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.comp;
|
||||
|
||||
import com.sun.tools.javac.comp.Resolve;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
|
||||
/** Subclass to Resolve that overrides collect.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class ResolveWithDeps extends Resolve {
|
||||
|
||||
/** The dependency database
|
||||
*/
|
||||
protected Dependencies deps;
|
||||
|
||||
protected ResolveWithDeps(Context context) {
|
||||
super(context);
|
||||
deps = Dependencies.instance(context);
|
||||
}
|
||||
|
||||
public static void preRegister(Context context) {
|
||||
context.put(resolveKey, new Context.Factory<Resolve>() {
|
||||
public Resolve make(Context c) {
|
||||
Resolve instance = new ResolveWithDeps(c);
|
||||
c.put(Resolve.class, instance);
|
||||
return instance;
|
||||
}
|
||||
});
|
||||
}
|
||||
/** Collect dependencies in the enclosing class
|
||||
* @param from The enclosing class sym
|
||||
* @param to The enclosing classes references this sym.
|
||||
* */
|
||||
@Override
|
||||
public void reportDependence(Symbol from, Symbol to) {
|
||||
// Capture dependencies between the packages.
|
||||
deps.collect(from.packge().fullname, to.packge().fullname);
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.comp;
|
||||
|
||||
import com.sun.tools.javac.util.ListBuffer;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import javax.tools.*;
|
||||
import javax.tools.JavaFileObject.Kind;
|
||||
|
||||
/**
|
||||
* Intercepts reads and writes to the file system to gather
|
||||
* information about what artifacts are generated.
|
||||
*
|
||||
* Traps writes to certain files, if the content written is identical
|
||||
* to the existing file.
|
||||
*
|
||||
* Can also blind out the filemanager from seeing certain files in the file system.
|
||||
* Necessary to prevent javac from seeing some sources where the source path points.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class SmartFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
|
||||
// Set of sources that can be seen by javac.
|
||||
Set<URI> visibleSources = new HashSet<URI>();
|
||||
// Map from modulename:packagename to artifacts.
|
||||
Map<String,Set<URI>> packageArtifacts = new HashMap<String,Set<URI>>();
|
||||
// Where to print informational messages.
|
||||
PrintWriter stdout;
|
||||
|
||||
public SmartFileManager(JavaFileManager fileManager) {
|
||||
super(fileManager);
|
||||
}
|
||||
|
||||
public void setVisibleSources(Set<URI> s) {
|
||||
visibleSources = s;
|
||||
}
|
||||
|
||||
public void cleanArtifacts() {
|
||||
packageArtifacts = new HashMap<String,Set<URI>>();
|
||||
}
|
||||
|
||||
public void setLog(PrintWriter pw) {
|
||||
stdout = pw;
|
||||
}
|
||||
|
||||
public Map<String,Set<URI>> getPackageArtifacts() {
|
||||
return packageArtifacts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<JavaFileObject> list(Location location,
|
||||
String packageName,
|
||||
Set<Kind> kinds,
|
||||
boolean recurse)
|
||||
throws IOException
|
||||
{
|
||||
// Acquire the list of files.
|
||||
Iterable<JavaFileObject> files = super.list(location, packageName, kinds, recurse);
|
||||
if (visibleSources.isEmpty()) {
|
||||
return files;
|
||||
}
|
||||
// Now filter!
|
||||
ListBuffer<JavaFileObject> filteredFiles = new ListBuffer<JavaFileObject>();
|
||||
for (JavaFileObject f : files) {
|
||||
URI uri = f.toUri();
|
||||
String t = uri.toString();
|
||||
if (t.startsWith("jar:")
|
||||
|| t.endsWith(".class")
|
||||
|| visibleSources.contains(uri))
|
||||
{
|
||||
filteredFiles.add(f);
|
||||
}
|
||||
}
|
||||
return filteredFiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLocation(Location location) {
|
||||
return super.hasLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForInput(Location location,
|
||||
String className,
|
||||
Kind kind)
|
||||
throws IOException
|
||||
{
|
||||
JavaFileObject file = super.getJavaFileForInput(location, className, kind);
|
||||
if (file == null || visibleSources.isEmpty()) {
|
||||
return file;
|
||||
}
|
||||
|
||||
if (visibleSources.contains(file.toUri())) {
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(Location location,
|
||||
String className,
|
||||
Kind kind,
|
||||
FileObject sibling)
|
||||
throws IOException
|
||||
{
|
||||
JavaFileObject file = super.getJavaFileForOutput(location, className, kind, sibling);
|
||||
if (file == null) return file;
|
||||
int dp = className.lastIndexOf('.');
|
||||
String pkg_name = "";
|
||||
if (dp != -1) {
|
||||
pkg_name = className.substring(0, dp);
|
||||
}
|
||||
// When modules are in use, then the mod_name might be something like "jdk_base"
|
||||
String mod_name = "";
|
||||
addArtifact(mod_name+":"+pkg_name, file.toUri());
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileObject getFileForInput(Location location,
|
||||
String packageName,
|
||||
String relativeName)
|
||||
throws IOException
|
||||
{
|
||||
FileObject file = super.getFileForInput(location, packageName, relativeName);
|
||||
if (file == null || visibleSources.isEmpty()) {
|
||||
return file;
|
||||
}
|
||||
|
||||
if (visibleSources.contains(file.toUri())) {
|
||||
return file;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileObject getFileForOutput(Location location,
|
||||
String packageName,
|
||||
String relativeName,
|
||||
FileObject sibling)
|
||||
throws IOException
|
||||
{
|
||||
FileObject file = super.getFileForOutput(location, packageName, relativeName, sibling);
|
||||
if (file == null) return file;
|
||||
if (location.equals(StandardLocation.NATIVE_HEADER_OUTPUT) &&
|
||||
file instanceof JavaFileObject) {
|
||||
file = new SmartFileObject((JavaFileObject)file, stdout);
|
||||
packageName = ":" + packageNameFromFileName(relativeName);
|
||||
}
|
||||
if (packageName.equals("")) {
|
||||
packageName = ":";
|
||||
}
|
||||
addArtifact(packageName, file.toUri());
|
||||
return file;
|
||||
}
|
||||
|
||||
private String packageNameFromFileName(String fn) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int p = fn.indexOf('_'), pp = 0;
|
||||
while (p != -1) {
|
||||
if (sb.length() > 0) sb.append('.');
|
||||
sb.append(fn.substring(pp,p));
|
||||
if (p == fn.length()-1) break;
|
||||
pp = p+1;
|
||||
p = fn.indexOf('_',pp);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
super.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
}
|
||||
|
||||
void addArtifact(String pkgName, URI art) {
|
||||
Set<URI> s = packageArtifacts.get(pkgName);
|
||||
if (s == null) {
|
||||
s = new HashSet<URI>();
|
||||
packageArtifacts.put(pkgName, s);
|
||||
}
|
||||
s.add(art);
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.comp;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.NestingKind;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
/**
|
||||
* The SmartFileObject will return an outputstream that cache the written data
|
||||
* and compare the new content with the old content on disk. Only if they differ,
|
||||
* will the file be updated.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class SmartFileObject implements JavaFileObject {
|
||||
|
||||
JavaFileObject file;
|
||||
PrintWriter stdout;
|
||||
|
||||
public SmartFileObject(JavaFileObject r, PrintWriter pw) {
|
||||
file = r;
|
||||
stdout = pw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return file.equals(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return file.hashCode();
|
||||
}
|
||||
|
||||
public Kind getKind() {
|
||||
return file.getKind();
|
||||
}
|
||||
|
||||
public boolean isNameCompatible(String simpleName, Kind kind) {
|
||||
return file.isNameCompatible(simpleName, kind);
|
||||
}
|
||||
|
||||
public URI toUri() {
|
||||
return file.toUri();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
public InputStream openInputStream() throws IOException {
|
||||
return file.openInputStream();
|
||||
}
|
||||
|
||||
public OutputStream openOutputStream() throws IOException {
|
||||
return file.openOutputStream();
|
||||
}
|
||||
|
||||
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
|
||||
return file.getCharContent(ignoreEncodingErrors);
|
||||
}
|
||||
|
||||
static String lineseparator = System.getProperty("line.separator");
|
||||
|
||||
public Writer openWriter() throws IOException {
|
||||
StringBuilder s = new StringBuilder();
|
||||
try (BufferedReader r = new BufferedReader(file.openReader(true))) {
|
||||
while (r.ready()) {
|
||||
s.append(r.readLine()+lineseparator);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// Perfectly ok.
|
||||
}
|
||||
return new SmartWriter(file, s.toString(), file.getName(), stdout);
|
||||
}
|
||||
|
||||
public long getLastModified() {
|
||||
return file.getLastModified();
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return file.delete();
|
||||
}
|
||||
|
||||
public Modifier getAccessLevel() {
|
||||
return file.getAccessLevel();
|
||||
}
|
||||
|
||||
public NestingKind getNestingKind() {
|
||||
return file.getNestingKind();
|
||||
}
|
||||
|
||||
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
|
||||
return file.openReader(ignoreEncodingErrors);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.comp;
|
||||
|
||||
import java.io.*;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
/**
|
||||
* The SmartWriter will cache the written data and when the writer is closed,
|
||||
* then it will compare the cached data with the old_content string.
|
||||
* If different, then it will write all the new content to the file.
|
||||
* If not, the file is not touched.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class SmartWriter extends Writer {
|
||||
|
||||
String name;
|
||||
JavaFileObject file;
|
||||
String oldContent;
|
||||
StringWriter newContent = new StringWriter();
|
||||
PrintWriter stdout;
|
||||
boolean closed;
|
||||
public SmartWriter(JavaFileObject f, String s, String n, PrintWriter pw) {
|
||||
name = n;
|
||||
file = f;
|
||||
oldContent = s;
|
||||
newContent = new StringWriter();
|
||||
stdout = pw;
|
||||
closed = false;
|
||||
}
|
||||
|
||||
public void write(char[] chars, int i, int i1)
|
||||
{
|
||||
newContent.write(chars, i, i1);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
String s = newContent.toString();
|
||||
if (!oldContent.equals(s)) {
|
||||
int p = file.getName().lastIndexOf(File.separatorChar);
|
||||
try (Writer writer = file.openWriter()) {
|
||||
writer.write(s);
|
||||
}
|
||||
stdout.println("Writing "+file.getName().substring(p+1));
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.server;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/** The compiler pool maintains compiler threads.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class CompilerPool {
|
||||
// The javac server that created this pool.
|
||||
private JavacServer javacServer;
|
||||
// A semaphore protecting the poolsize number of threads.
|
||||
private Semaphore available;
|
||||
// The stack of compiler threads.
|
||||
private Stack<CompilerThread> compilers = new Stack<CompilerThread>();
|
||||
// And the executor server to spawn threads.
|
||||
private final ExecutorService executorPool;
|
||||
// How many requests are active right now?
|
||||
private int concurrentRequests = 0;
|
||||
// When was the last request finished?
|
||||
private long lastRequestFinished = 0;
|
||||
// The total number of requests to this pool.
|
||||
private int numRequests = 0;
|
||||
// Protect access to the three above values.
|
||||
private static final Object conc = new Object();
|
||||
|
||||
/**
|
||||
* Return the javac server that this pool belongs to.
|
||||
*/
|
||||
public JavacServer getJavacServer() {
|
||||
return javacServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return how many threads are running at this very moment.
|
||||
*/
|
||||
public int numActiveRequests()
|
||||
{
|
||||
synchronized (conc) {
|
||||
return concurrentRequests;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return when the last request was finished.
|
||||
* I.e. the pool has been idle since.
|
||||
*/
|
||||
public long lastRequestFinished()
|
||||
{
|
||||
synchronized (conc) {
|
||||
return lastRequestFinished;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Up the number of active requests.
|
||||
*/
|
||||
public int startRequest() {
|
||||
int n;
|
||||
synchronized (conc) {
|
||||
concurrentRequests++;
|
||||
numRequests++;
|
||||
n = numRequests;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Down the number of active requests. Return the current time.
|
||||
*/
|
||||
public long stopRequest() {
|
||||
synchronized (conc) {
|
||||
concurrentRequests--;
|
||||
lastRequestFinished = System.currentTimeMillis();
|
||||
}
|
||||
return lastRequestFinished;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new compiler pool.
|
||||
*/
|
||||
CompilerPool(int poolsize, JavacServer server) {
|
||||
available = new Semaphore(poolsize, true);
|
||||
javacServer = server;
|
||||
executorPool = Executors.newFixedThreadPool(poolsize);
|
||||
lastRequestFinished = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a compiler thread.
|
||||
*/
|
||||
public void execute(CompilerThread ct) {
|
||||
executorPool.execute(ct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a minor task, for example generating bytecodes and writing them to disk,
|
||||
* that belong to a major compiler thread task.
|
||||
*/
|
||||
public Future<?> executeSubtask(CompilerThread t, Runnable r) {
|
||||
return executorPool.submit(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the pool.
|
||||
*/
|
||||
public void shutdown() {
|
||||
executorPool.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a compiler thread from the pool, or block until a thread is available.
|
||||
* If the pools is empty, create a new thread, but never more than is "available".
|
||||
*/
|
||||
public CompilerThread grabCompilerThread() throws InterruptedException {
|
||||
available.acquire();
|
||||
if (compilers.empty()) {
|
||||
return new CompilerThread(this);
|
||||
}
|
||||
return compilers.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specified compiler thread to the pool.
|
||||
*/
|
||||
public void returnCompilerThread(CompilerThread h) {
|
||||
compilers.push(h);
|
||||
available.release();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.server;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.net.Socket;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Future;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.Log;
|
||||
import com.sun.tools.javac.util.BaseFileManager;
|
||||
import com.sun.tools.sjavac.comp.Dependencies;
|
||||
import com.sun.tools.sjavac.comp.JavaCompilerWithDeps;
|
||||
import com.sun.tools.sjavac.comp.SmartFileManager;
|
||||
import com.sun.tools.sjavac.comp.ResolveWithDeps;
|
||||
|
||||
/**
|
||||
* The compiler thread maintains a JavaCompiler instance and
|
||||
* can receive a request from the client, perform the compilation
|
||||
* requested and report back the results.
|
||||
*
|
||||
* * <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></p>
|
||||
*/
|
||||
public class CompilerThread implements Runnable {
|
||||
private JavacServer javacServer;
|
||||
private CompilerPool compilerPool;
|
||||
private List<Future<?>> subTasks;
|
||||
|
||||
// Communicating over this socket.
|
||||
private Socket socket;
|
||||
|
||||
// The necessary classes to do a compilation.
|
||||
private com.sun.tools.javac.api.JavacTool compiler;
|
||||
private StandardJavaFileManager fileManager;
|
||||
private BaseFileManager fileManagerBase;
|
||||
private SmartFileManager smartFileManager;
|
||||
private Context context;
|
||||
|
||||
// If true, then this thread is serving a request.
|
||||
private boolean inUse = false;
|
||||
|
||||
CompilerThread(CompilerPool cp) {
|
||||
compilerPool = cp;
|
||||
javacServer = cp.getJavacServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a minor task, for example generating bytecodes and writing them to disk,
|
||||
* that belong to a major compiler thread task.
|
||||
*/
|
||||
public synchronized void executeSubtask(Runnable r) {
|
||||
subTasks.add(compilerPool.executeSubtask(this, r));
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of active sub tasks.
|
||||
*/
|
||||
public synchronized int numActiveSubTasks() {
|
||||
int c = 0;
|
||||
for (Future<?> f : subTasks) {
|
||||
if (!f.isDone() && !f.isCancelled()) {
|
||||
c++;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this socket for the upcoming request.
|
||||
*/
|
||||
public void setSocket(Socket s) {
|
||||
socket = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the compiler thread for use. It is not yet started.
|
||||
* It will be started by the executor service.
|
||||
*/
|
||||
public synchronized void use() {
|
||||
assert(!inUse);
|
||||
inUse = true;
|
||||
compiler = com.sun.tools.javac.api.JavacTool.create();
|
||||
fileManager = compiler.getStandardFileManager(null, null, null);
|
||||
fileManagerBase = (BaseFileManager)fileManager;
|
||||
smartFileManager = new SmartFileManager(fileManager);
|
||||
context = new Context();
|
||||
context.put(JavaFileManager.class, smartFileManager);
|
||||
ResolveWithDeps.preRegister(context);
|
||||
JavaCompilerWithDeps.preRegister(context, this);
|
||||
subTasks = new ArrayList<Future<?>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the compiler thread for idleness.
|
||||
*/
|
||||
public synchronized void unuse() {
|
||||
assert(inUse);
|
||||
inUse = false;
|
||||
compiler = null;
|
||||
fileManager = null;
|
||||
fileManagerBase = null;
|
||||
smartFileManager = null;
|
||||
context = null;
|
||||
subTasks = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expect this key on the next line read from the reader.
|
||||
*/
|
||||
private static boolean expect(BufferedReader in, String key) throws IOException {
|
||||
String s = in.readLine();
|
||||
if (s != null && s.equals(key)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The request identifier, for example GENERATE_NEWBYTECODE
|
||||
String id = "";
|
||||
|
||||
public String currentRequestId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
PrintWriter stdout;
|
||||
PrintWriter stderr;
|
||||
int forcedExitCode = 0;
|
||||
|
||||
public void logError(String msg) {
|
||||
stderr.println(msg);
|
||||
forcedExitCode = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by the executor service.
|
||||
*/
|
||||
public void run() {
|
||||
// Unique nr that identifies this request.
|
||||
int thisRequest = compilerPool.startRequest();
|
||||
long start = System.currentTimeMillis();
|
||||
int numClasses = 0;
|
||||
StringBuilder compiledPkgs = new StringBuilder();
|
||||
use();
|
||||
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
javacServer.log("<"+thisRequest+"> Connect from "+socket.getRemoteSocketAddress()+" activethreads="+compilerPool.numActiveRequests());
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(
|
||||
socket.getInputStream()));
|
||||
out = new PrintWriter(new OutputStreamWriter(
|
||||
socket.getOutputStream()));
|
||||
if (!expect(in, JavacServer.PROTOCOL_COOKIE_VERSION)) {
|
||||
javacServer.log("<"+thisRequest+"> Bad protocol from ip "+socket.getRemoteSocketAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
String cookie = in.readLine();
|
||||
if (cookie == null || !cookie.equals(""+javacServer.getCookie())) {
|
||||
javacServer.log("<"+thisRequest+"> Bad cookie from ip "+socket.getRemoteSocketAddress());
|
||||
return;
|
||||
}
|
||||
if (!expect(in, JavacServer.PROTOCOL_CWD)) {
|
||||
return;
|
||||
}
|
||||
String cwd = in.readLine();
|
||||
if (cwd == null)
|
||||
return;
|
||||
if (!expect(in, JavacServer.PROTOCOL_ID)) {
|
||||
return;
|
||||
}
|
||||
id = in.readLine();
|
||||
if (id == null)
|
||||
return;
|
||||
if (!expect(in, JavacServer.PROTOCOL_ARGS)) {
|
||||
return;
|
||||
}
|
||||
ArrayList<String> the_options = new ArrayList<String>();
|
||||
ArrayList<File> the_classes = new ArrayList<File>();
|
||||
Iterable<File> path = Arrays.<File> asList(new File(cwd));
|
||||
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null)
|
||||
return;
|
||||
if (l.equals(JavacServer.PROTOCOL_SOURCES_TO_COMPILE))
|
||||
break;
|
||||
if (l.startsWith("--server:"))
|
||||
continue;
|
||||
if (!l.startsWith("-") && l.endsWith(".java")) {
|
||||
the_classes.add(new File(l));
|
||||
numClasses++;
|
||||
} else {
|
||||
the_options.add(l);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load sources to compile
|
||||
Set<URI> sourcesToCompile = new HashSet<URI>();
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null)
|
||||
return;
|
||||
if (l.equals(JavacServer.PROTOCOL_VISIBLE_SOURCES))
|
||||
break;
|
||||
try {
|
||||
sourcesToCompile.add(new URI(l));
|
||||
numClasses++;
|
||||
} catch (URISyntaxException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Load visible sources
|
||||
Set<URI> visibleSources = new HashSet<URI>();
|
||||
boolean fix_drive_letter_case = System.getProperty("os.name").toLowerCase().equals("windows");
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null)
|
||||
return;
|
||||
if (l.equals(JavacServer.PROTOCOL_END))
|
||||
break;
|
||||
try {
|
||||
URI u = new URI(l);
|
||||
if (fix_drive_letter_case) {
|
||||
// Make sure the driver letter is lower case.
|
||||
String s = u.toString();
|
||||
if (s.startsWith("file:/") &&
|
||||
Character.isUpperCase(s.charAt(6))) {
|
||||
u = new URI("file:/"+Character.toLowerCase(s.charAt(6))+s.substring(7));
|
||||
}
|
||||
}
|
||||
visibleSources.add(u);
|
||||
} catch (URISyntaxException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// A completed request has been received.
|
||||
|
||||
// Now setup the actual compilation....
|
||||
// First deal with explicit source files on cmdline and in at file.
|
||||
com.sun.tools.javac.util.ListBuffer<JavaFileObject> compilationUnits =
|
||||
new com.sun.tools.javac.util.ListBuffer<JavaFileObject>();
|
||||
for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(the_classes)) {
|
||||
compilationUnits.append(i);
|
||||
}
|
||||
// Now deal with sources supplied as source_to_compile.
|
||||
com.sun.tools.javac.util.ListBuffer<File> sourcesToCompileFiles =
|
||||
new com.sun.tools.javac.util.ListBuffer<File>();
|
||||
for (URI u : sourcesToCompile) {
|
||||
sourcesToCompileFiles.append(new File(u));
|
||||
}
|
||||
for (JavaFileObject i : fileManager.getJavaFileObjectsFromFiles(sourcesToCompileFiles)) {
|
||||
compilationUnits.append(i);
|
||||
}
|
||||
// Log the options to be used.
|
||||
StringBuilder options = new StringBuilder();
|
||||
for (String s : the_options) {
|
||||
options.append(">").append(s).append("< ");
|
||||
}
|
||||
javacServer.log(id+" <"+thisRequest+"> options "+options.toString());
|
||||
|
||||
forcedExitCode = 0;
|
||||
// Create a new logger.
|
||||
StringWriter stdoutLog = new StringWriter();
|
||||
StringWriter stderrLog = new StringWriter();
|
||||
stdout = new PrintWriter(stdoutLog);
|
||||
stderr = new PrintWriter(stderrLog);
|
||||
com.sun.tools.javac.main.Main.Result rc = com.sun.tools.javac.main.Main.Result.OK;
|
||||
try {
|
||||
if (compilationUnits.size() > 0) {
|
||||
// Bind the new logger to the existing context.
|
||||
context.put(Log.outKey, stderr);
|
||||
Log.instance(context).setWriter(Log.WriterKind.NOTICE, stdout);
|
||||
Log.instance(context).setWriter(Log.WriterKind.WARNING, stderr);
|
||||
Log.instance(context).setWriter(Log.WriterKind.ERROR, stderr);
|
||||
// Process the options.
|
||||
com.sun.tools.javac.api.JavacTool.processOptions(context, smartFileManager, the_options);
|
||||
fileManagerBase.setContext(context);
|
||||
smartFileManager.setVisibleSources(visibleSources);
|
||||
smartFileManager.cleanArtifacts();
|
||||
smartFileManager.setLog(stdout);
|
||||
Dependencies.instance(context).reset();
|
||||
|
||||
com.sun.tools.javac.main.Main ccompiler = new com.sun.tools.javac.main.Main("javacTask", stderr);
|
||||
String[] aa = the_options.toArray(new String[0]);
|
||||
|
||||
// Do the compilation!
|
||||
rc = ccompiler.compile(aa, context, compilationUnits.toList(), null);
|
||||
|
||||
while (numActiveSubTasks()>0) {
|
||||
try { Thread.sleep(1000); } catch (InterruptedException e) { }
|
||||
}
|
||||
|
||||
smartFileManager.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
stderr.println(e.getMessage());
|
||||
forcedExitCode = -1;
|
||||
}
|
||||
|
||||
// Send the response..
|
||||
out.println(JavacServer.PROTOCOL_STDOUT);
|
||||
out.print(stdoutLog);
|
||||
out.println(JavacServer.PROTOCOL_STDERR);
|
||||
out.print(stderrLog);
|
||||
// The compilation is complete! And errors will have already been printed on out!
|
||||
out.println(JavacServer.PROTOCOL_PACKAGE_ARTIFACTS);
|
||||
Map<String,Set<URI>> pa = smartFileManager.getPackageArtifacts();
|
||||
for (String aPkgName : pa.keySet()) {
|
||||
out.println("+"+aPkgName);
|
||||
Set<URI> as = pa.get(aPkgName);
|
||||
for (URI a : as) {
|
||||
out.println(" "+a.toString());
|
||||
}
|
||||
}
|
||||
Dependencies deps = Dependencies.instance(context);
|
||||
out.println(JavacServer.PROTOCOL_PACKAGE_DEPENDENCIES);
|
||||
Map<String,Set<String>> pd = deps.getDependencies();
|
||||
for (String aPkgName : pd.keySet()) {
|
||||
out.println("+"+aPkgName);
|
||||
Set<String> ds = pd.get(aPkgName);
|
||||
// Everything depends on java.lang
|
||||
if (!ds.contains(":java.lang")) ds.add(":java.lang");
|
||||
for (String d : ds) {
|
||||
out.println(" "+d);
|
||||
}
|
||||
}
|
||||
out.println(JavacServer.PROTOCOL_PACKAGE_PUBLIC_APIS);
|
||||
Map<String,String> pp = deps.getPubapis();
|
||||
for (String aPkgName : pp.keySet()) {
|
||||
out.println("+"+aPkgName);
|
||||
String ps = pp.get(aPkgName);
|
||||
// getPubapis added a space to each line!
|
||||
out.println(ps);
|
||||
compiledPkgs.append(aPkgName+" ");
|
||||
}
|
||||
out.println(JavacServer.PROTOCOL_SYSINFO);
|
||||
out.println("num_cores=" + Runtime.getRuntime().availableProcessors());
|
||||
out.println("max_memory=" + Runtime.getRuntime().maxMemory());
|
||||
out.println(JavacServer.PROTOCOL_RETURN_CODE);
|
||||
|
||||
// Errors from sjavac that affect compilation status!
|
||||
int rcv = rc.exitCode;
|
||||
if (rcv == 0 && forcedExitCode != 0) {
|
||||
rcv = forcedExitCode;
|
||||
}
|
||||
out.println("" + rcv);
|
||||
out.println(JavacServer.PROTOCOL_END);
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) out.close();
|
||||
if (!socket.isClosed()) {
|
||||
socket.close();
|
||||
}
|
||||
socket = null;
|
||||
} catch (Exception e) {
|
||||
javacServer.log("ERROR "+e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
compilerPool.stopRequest();
|
||||
long duration = System.currentTimeMillis()-start;
|
||||
javacServer.addBuildTime(duration);
|
||||
float classpersec = ((float)numClasses)*(((float)1000.0)/((float)duration));
|
||||
javacServer.log(id+" <"+thisRequest+"> "+compiledPkgs+" duration " + duration+ " ms num_classes="+numClasses+
|
||||
" classpersec="+classpersec+" subtasks="+subTasks.size());
|
||||
javacServer.flushLog();
|
||||
unuse();
|
||||
compilerPool.returnCompilerThread(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,751 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012, 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.server;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.URI;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import com.sun.tools.sjavac.Util;
|
||||
import com.sun.tools.sjavac.ProblemException;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The JavacServer class contains methods both to setup a server that responds to requests and methods to connect to this server.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
public class JavacServer {
|
||||
// Responding to this tcp/ip port on localhost.
|
||||
|
||||
private final ServerSocket serverSocket;
|
||||
// The secret cookie shared between server and client through the port file.
|
||||
private final long myCookie;
|
||||
// When the server was started.
|
||||
private long serverStart;
|
||||
// Accumulated build time for all requests, not counting idle time.
|
||||
private long totalBuildTime;
|
||||
// The javac server specific log file.
|
||||
PrintWriter theLog;
|
||||
// The compiler pool that maintains the compiler threads.
|
||||
CompilerPool compilerPool;
|
||||
// For the client, all port files fetched, one per started javac server.
|
||||
// Though usually only one javac server is started by a client.
|
||||
private static Map<String, PortFile> allPortFiles;
|
||||
private static Map<String, Long> maxServerMemory;
|
||||
final static int ERROR_FATAL = -1;
|
||||
final static int ERROR_BUT_TRY_AGAIN = -4712;
|
||||
final static String PROTOCOL_COOKIE_VERSION = "----THE-COOKIE-V2----";
|
||||
final static String PROTOCOL_CWD = "----THE-CWD----";
|
||||
final static String PROTOCOL_ID = "----THE-ID----";
|
||||
final static String PROTOCOL_ARGS = "----THE-ARGS----";
|
||||
final static String PROTOCOL_SOURCES_TO_COMPILE = "----THE-SOURCES-TO-COMPILE----";
|
||||
final static String PROTOCOL_VISIBLE_SOURCES = "----THE-VISIBLE-SOURCES----";
|
||||
final static String PROTOCOL_END = "----THE-END----";
|
||||
final static String PROTOCOL_STDOUT = "----THE-STDOUT----";
|
||||
final static String PROTOCOL_STDERR = "----THE-STDERR----";
|
||||
final static String PROTOCOL_PACKAGE_ARTIFACTS = "----THE-PACKAGE_ARTIFACTS----";
|
||||
final static String PROTOCOL_PACKAGE_DEPENDENCIES = "----THE-PACKAGE_DEPENDENCIES----";
|
||||
final static String PROTOCOL_PACKAGE_PUBLIC_APIS = "----THE-PACKAGE-PUBLIC-APIS----";
|
||||
final static String PROTOCOL_SYSINFO = "----THE-SYSINFO----";
|
||||
final static String PROTOCOL_RETURN_CODE = "----THE-RETURN-CODE----";
|
||||
// Check if the portfile is gone, every 5 seconds.
|
||||
static int CHECK_PORTFILE_INTERVAL = 5;
|
||||
// Wait 2 seconds for response, before giving up on javac server.
|
||||
static int CONNECTION_TIMEOUT = 2;
|
||||
static int WAIT_BETWEEN_CONNECT_ATTEMPTS = 1;
|
||||
static int MAX_NUM_CONNECT_ATTEMPTS = 3;
|
||||
|
||||
/**
|
||||
* Acquire the port file. Synchronized since several threads inside an smart javac wrapper client acquires the same port file at the same time.
|
||||
*/
|
||||
private static synchronized PortFile getPortFile(String filename) throws FileNotFoundException {
|
||||
if (allPortFiles == null) {
|
||||
allPortFiles = new HashMap<String, PortFile>();
|
||||
}
|
||||
PortFile pf = allPortFiles.get(filename);
|
||||
if (pf == null) {
|
||||
pf = new PortFile(filename);
|
||||
allPortFiles.put(filename, pf);
|
||||
}
|
||||
return pf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cookie used for this server.
|
||||
*/
|
||||
long getCookie() {
|
||||
return myCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port used for this server.
|
||||
*/
|
||||
int getPort() {
|
||||
return serverSocket.getLocalPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum up the total build time for this javac server.
|
||||
*/
|
||||
public void addBuildTime(long inc) {
|
||||
totalBuildTime += inc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log this message.
|
||||
*/
|
||||
public void log(String msg) {
|
||||
if (theLog != null) {
|
||||
theLog.println(msg);
|
||||
} else {
|
||||
System.err.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the log is flushed.
|
||||
*/
|
||||
public void flushLog() {
|
||||
if (theLog != null) {
|
||||
theLog.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a server using a settings string. Typically: "--startserver:portfile=/tmp/myserver,poolsize=3" and the string "portfile=/tmp/myserver,poolsize=3"
|
||||
* is sent as the settings parameter. Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
public static int startServer(String settings, PrintStream err) {
|
||||
try {
|
||||
String portfile = Util.extractStringOption("portfile", settings);
|
||||
// The log file collects more javac server specific log information.
|
||||
String logfile = Util.extractStringOption("logfile", settings);
|
||||
// The stdouterr file collects all the System.out and System.err writes to disk.
|
||||
String stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
|
||||
// We could perhaps use System.setOut and setErr here.
|
||||
// But for the moment we rely on the client to spawn a shell where stdout
|
||||
// and stderr are redirected already.
|
||||
// The pool size is a limit the number of concurrent compiler threads used.
|
||||
// The server might use less than these to avoid memory problems.
|
||||
int poolsize = Util.extractIntOption("poolsize", settings);
|
||||
if (poolsize <= 0) {
|
||||
// If not set, default to the number of cores.
|
||||
poolsize = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
|
||||
// How many seconds of inactivity will the server accept before quitting?
|
||||
int keepalive = Util.extractIntOption("keepalive", settings);
|
||||
if (keepalive <= 0) {
|
||||
keepalive = 120;
|
||||
}
|
||||
// The port file is locked and the server port and cookie is written into it.
|
||||
PortFile portFile = getPortFile(portfile);
|
||||
JavacServer s;
|
||||
|
||||
synchronized (portFile) {
|
||||
portFile.lock();
|
||||
portFile.getValues();
|
||||
if (portFile.containsPortInfo()) {
|
||||
err.println("Javac server not started because portfile exists!");
|
||||
portFile.unlock();
|
||||
return -1;
|
||||
}
|
||||
s = new JavacServer(poolsize, logfile);
|
||||
portFile.setValues(s.getPort(), s.getCookie());
|
||||
portFile.unlock();
|
||||
}
|
||||
|
||||
// Run the server. Will delete the port file when shutting down.
|
||||
// It will shut down automatically when no new requests have come in
|
||||
// during the last 125 seconds.
|
||||
s.run(portFile, err, keepalive);
|
||||
// The run loop for the server has exited.
|
||||
return 0;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a compilation request to a javac server.
|
||||
*
|
||||
* @param args are the command line args to javac and is allowed to contain source files, @file and other command line options to javac.
|
||||
*
|
||||
* The generated classes, h files and other artifacts from the javac invocation are stored by the javac server to disk.
|
||||
*
|
||||
* @param sources_to_compile The sources to compile.
|
||||
*
|
||||
* @param visibleSources If visible sources has a non zero size, then visible_sources are the only files in the file system that the javac server can see!
|
||||
* (Sources to compile are always visible.) The visible sources are those supplied by the (filtered) -sourcepath
|
||||
*
|
||||
* @param visibleClasses If visible classes for a specific root/jar has a non zero size, then visible_classes are the only class files that the javac server
|
||||
* can see, in that root/jar. It maps from a classpath root or a jar file to the set of visible classes for that root/jar.
|
||||
*
|
||||
* The server return meta data about the build in the following parameters.
|
||||
* @param package_artifacts, map from package name to set of created artifacts for that package.
|
||||
* @param package_dependencies, map from package name to set of packages that it depends upon.
|
||||
* @param package_pubapis, map from package name to unique string identifying its pub api.
|
||||
*/
|
||||
public static int useServer(String settings, String[] args,
|
||||
Set<URI> sourcesToCompile,
|
||||
Set<URI> visibleSources,
|
||||
Map<URI, Set<String>> visibleClasses,
|
||||
Map<String, Set<URI>> packageArtifacts,
|
||||
Map<String, Set<String>> packageDependencies,
|
||||
Map<String, String> packagePubapis,
|
||||
SysInfo sysinfo,
|
||||
PrintStream out,
|
||||
PrintStream err) {
|
||||
try {
|
||||
// The id can perhaps be used in the future by the javac server to reuse the
|
||||
// JavaCompiler instance for several compiles using the same id.
|
||||
String id = Util.extractStringOption("id", settings);
|
||||
String portfile = Util.extractStringOption("portfile", settings);
|
||||
String logfile = Util.extractStringOption("logfile", settings);
|
||||
String stdouterrfile = Util.extractStringOption("stdouterrfile", settings);
|
||||
String background = Util.extractStringOption("background", settings);
|
||||
if (background == null || !background.equals("false")) {
|
||||
background = "true";
|
||||
}
|
||||
// The sjavac option specifies how the server part of sjavac is spawned.
|
||||
// If you have the experimental sjavac in your path, you are done. If not, you have
|
||||
// to point to a com.sun.tools.sjavac.Main that supports --startserver
|
||||
// for example by setting: sjavac=java%20-jar%20...javac.jar%com.sun.tools.sjavac.Main
|
||||
String sjavac = Util.extractStringOption("sjavac", settings);
|
||||
int poolsize = Util.extractIntOption("poolsize", settings);
|
||||
int keepalive = Util.extractIntOption("keepalive", settings);
|
||||
|
||||
if (keepalive <= 0) {
|
||||
// Default keepalive for server is 120 seconds.
|
||||
// I.e. it will accept 120 seconds of inactivity before quitting.
|
||||
keepalive = 120;
|
||||
}
|
||||
if (portfile == null) {
|
||||
err.println("No portfile was specified!");
|
||||
return -1;
|
||||
}
|
||||
if (logfile == null) {
|
||||
logfile = portfile + ".javaclog";
|
||||
}
|
||||
if (stdouterrfile == null) {
|
||||
stdouterrfile = portfile + ".stdouterr";
|
||||
}
|
||||
// Default to sjavac and hope it is in the path.
|
||||
if (sjavac == null) {
|
||||
sjavac = "sjavac";
|
||||
}
|
||||
|
||||
int attempts = 0;
|
||||
int rc = -1;
|
||||
do {
|
||||
PortFile port_file = getPortFile(portfile);
|
||||
synchronized (port_file) {
|
||||
port_file.lock();
|
||||
port_file.getValues();
|
||||
port_file.unlock();
|
||||
}
|
||||
if (!port_file.containsPortInfo()) {
|
||||
String cmd = fork(sjavac, port_file.getFilename(), logfile, poolsize, keepalive, err, stdouterrfile, background);
|
||||
|
||||
if (background.equals("true") && !port_file.waitForValidValues()) {
|
||||
// Ouch the server did not start! Lets print its stdouterrfile and the command used.
|
||||
printFailedAttempt(cmd, stdouterrfile, err);
|
||||
// And give up.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
rc = connectAndCompile(port_file, id, args, sourcesToCompile, visibleSources,
|
||||
packageArtifacts, packageDependencies, packagePubapis, sysinfo,
|
||||
out, err);
|
||||
// Try again until we manage to connect. Any error after that
|
||||
// will cause the compilation to fail.
|
||||
if (rc == ERROR_BUT_TRY_AGAIN) {
|
||||
// We could not connect to the server. Try again.
|
||||
attempts++;
|
||||
try {
|
||||
Thread.sleep(WAIT_BETWEEN_CONNECT_ATTEMPTS);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
} while (rc == ERROR_BUT_TRY_AGAIN && attempts < MAX_NUM_CONNECT_ATTEMPTS);
|
||||
return rc;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void printFailedAttempt(String cmd, String f, PrintStream err) {
|
||||
err.println("---- Failed to start javac server with this command -----");
|
||||
err.println(cmd);
|
||||
try {
|
||||
BufferedReader in = new BufferedReader(new FileReader(f));
|
||||
err.println("---- stdout/stderr output from attempt to start javac server -----");
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null) {
|
||||
break;
|
||||
}
|
||||
err.println(l);
|
||||
}
|
||||
err.println("------------------------------------------------------------------");
|
||||
} catch (Exception e) {
|
||||
err.println("The stdout/stderr output in file " + f + " does not exist and the server did not start.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn the server instance.
|
||||
*/
|
||||
|
||||
private JavacServer(int poolSize, String logfile) throws IOException {
|
||||
serverStart = System.currentTimeMillis();
|
||||
// Create a server socket on a random port that is bound to the localhost/127.0.0.1 interface.
|
||||
// I.e only local processes can connect to this port.
|
||||
serverSocket = new ServerSocket(0, 128, InetAddress.getByName(null));
|
||||
compilerPool = new CompilerPool(poolSize, this);
|
||||
Random rnd = new Random();
|
||||
myCookie = rnd.nextLong();
|
||||
theLog = new PrintWriter(logfile);
|
||||
log("Javac server started. port=" + getPort() + " date=" + (new java.util.Date()) + " with poolsize=" + poolSize);
|
||||
flushLog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork a background process. Returns the command line used that can be printed if something failed.
|
||||
*/
|
||||
private static String fork(String sjavac, String portfile, String logfile, int poolsize, int keepalive,
|
||||
final PrintStream err, String stdouterrfile, String background)
|
||||
throws IOException, ProblemException {
|
||||
if (stdouterrfile != null && stdouterrfile.trim().equals("")) {
|
||||
stdouterrfile = null;
|
||||
}
|
||||
final String startserver = "--startserver:portfile=" + portfile + ",logfile=" + logfile + ",stdouterrfile=" + stdouterrfile + ",poolsize=" + poolsize + ",keepalive="+ keepalive;
|
||||
|
||||
if (background.equals("true")) {
|
||||
sjavac += "%20" + startserver;
|
||||
sjavac = sjavac.replaceAll("%20", " ");
|
||||
sjavac = sjavac.replaceAll("%2C", ",");
|
||||
// If the java/sh/cmd launcher fails the failure will be captured by stdouterr because of the redirection here.
|
||||
String[] cmd = {"/bin/sh", "-c", sjavac + " >> " + stdouterrfile + " 2>&1"};
|
||||
if (!(new File("/bin/sh")).canExecute()) {
|
||||
ArrayList<String> wincmd = new ArrayList<String>();
|
||||
wincmd.add("cmd");
|
||||
wincmd.add("/c");
|
||||
wincmd.add("start");
|
||||
wincmd.add("cmd");
|
||||
wincmd.add("/c");
|
||||
wincmd.add(sjavac + " >> " + stdouterrfile + " 2>&1");
|
||||
cmd = wincmd.toArray(new String[wincmd.size()]);
|
||||
}
|
||||
Process pp = null;
|
||||
try {
|
||||
pp = Runtime.getRuntime().exec(cmd);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(err);
|
||||
e.printStackTrace(new PrintWriter(stdouterrfile));
|
||||
}
|
||||
StringBuilder rs = new StringBuilder();
|
||||
for (String s : cmd) {
|
||||
rs.append(s + " ");
|
||||
}
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
// Do not spawn a background server, instead run it within the same JVM.
|
||||
Thread t = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
JavacServer.startServer(startserver, err);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Expect this key on the next line read from the reader.
|
||||
*/
|
||||
private static boolean expect(BufferedReader in, String key) throws IOException {
|
||||
String s = in.readLine();
|
||||
if (s != null && s.equals(key)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a request to the server only to get the maximum possible heap size to use for compilations.
|
||||
*
|
||||
* @param port_file The port file used to synchronize creation of this server.
|
||||
* @param id The identify of the compilation.
|
||||
* @param out Standard out information.
|
||||
* @param err Standard err information.
|
||||
* @return The maximum heap size in bytes.
|
||||
*/
|
||||
public static SysInfo connectGetSysInfo(String serverSettings, PrintStream out, PrintStream err) {
|
||||
SysInfo sysinfo = new SysInfo(-1, -1);
|
||||
String id = Util.extractStringOption("id", serverSettings);
|
||||
String portfile = Util.extractStringOption("portfile", serverSettings);
|
||||
try {
|
||||
PortFile pf = getPortFile(portfile);
|
||||
useServer(serverSettings, new String[0],
|
||||
new HashSet<URI>(),
|
||||
new HashSet<URI>(),
|
||||
new HashMap<URI, Set<String>>(),
|
||||
new HashMap<String, Set<URI>>(),
|
||||
new HashMap<String, Set<String>>(),
|
||||
new HashMap<String, String>(),
|
||||
sysinfo, out, err);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(err);
|
||||
}
|
||||
return sysinfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect and compile using the javac server settings and the args. When using more advanced features, the sources_to_compile and visible_sources are
|
||||
* supplied to the server and meta data is returned in package_artifacts, package_dependencies and package_pubapis.
|
||||
*/
|
||||
private static int connectAndCompile(PortFile portFile, String id, String[] args,
|
||||
Set<URI> sourcesToCompile,
|
||||
Set<URI> visibleSources,
|
||||
Map<String, Set<URI>> packageArtifacts,
|
||||
Map<String, Set<String>> packageDependencies,
|
||||
Map<String, String> packagePublicApis,
|
||||
SysInfo sysinfo,
|
||||
PrintStream out,
|
||||
PrintStream err) {
|
||||
int rc = -3;
|
||||
try {
|
||||
int port = portFile.getPort();
|
||||
if (port == 0) {
|
||||
return ERROR_BUT_TRY_AGAIN;
|
||||
}
|
||||
long cookie = portFile.getCookie();
|
||||
|
||||
// Acquire the localhost/127.0.0.1 address.
|
||||
InetAddress addr = InetAddress.getByName(null);
|
||||
SocketAddress sockaddr = new InetSocketAddress(addr, port);
|
||||
Socket sock = new Socket();
|
||||
int timeoutMs = CONNECTION_TIMEOUT * 1000;
|
||||
try {
|
||||
sock.connect(sockaddr, timeoutMs);
|
||||
} catch (java.net.ConnectException e) {
|
||||
err.println("Could not connect to javac server found in portfile: " + portFile.getFilename() + " " + e);
|
||||
return ERROR_BUT_TRY_AGAIN;
|
||||
}
|
||||
if (!sock.isConnected()) {
|
||||
err.println("Could not connect to javac server found in portfile: " + portFile.getFilename());
|
||||
return ERROR_BUT_TRY_AGAIN;
|
||||
}
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
|
||||
PrintWriter sockout = new PrintWriter(sock.getOutputStream());
|
||||
|
||||
sockout.println(PROTOCOL_COOKIE_VERSION);
|
||||
sockout.println("" + cookie);
|
||||
sockout.println(PROTOCOL_CWD);
|
||||
sockout.println(System.getProperty("user.dir"));
|
||||
sockout.println(PROTOCOL_ID);
|
||||
sockout.println(id);
|
||||
sockout.println(PROTOCOL_ARGS);
|
||||
for (String s : args) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
String[] paths = s.split(File.pathSeparator);
|
||||
int c = 0;
|
||||
for (String path : paths) {
|
||||
File f = new File(path);
|
||||
if (f.isFile() || f.isDirectory()) {
|
||||
buf.append(f.getAbsolutePath());
|
||||
c++;
|
||||
if (c < paths.length) {
|
||||
buf.append(File.pathSeparator);
|
||||
}
|
||||
} else {
|
||||
buf = new StringBuffer(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sockout.println(buf.toString());
|
||||
}
|
||||
sockout.println(PROTOCOL_SOURCES_TO_COMPILE);
|
||||
for (URI uri : sourcesToCompile) {
|
||||
sockout.println(uri.toString());
|
||||
}
|
||||
sockout.println(PROTOCOL_VISIBLE_SOURCES);
|
||||
for (URI uri : visibleSources) {
|
||||
sockout.println(uri.toString());
|
||||
}
|
||||
sockout.println(PROTOCOL_END);
|
||||
sockout.flush();
|
||||
|
||||
StringBuffer stdout = new StringBuffer();
|
||||
StringBuffer stderr = new StringBuffer();
|
||||
|
||||
if (!expect(in, PROTOCOL_STDOUT)) {
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
// Load stdout
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null) {
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
if (l.equals(PROTOCOL_STDERR)) {
|
||||
break;
|
||||
}
|
||||
stdout.append(l);
|
||||
stdout.append('\n');
|
||||
}
|
||||
// Load stderr
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null) {
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
if (l.equals(PROTOCOL_PACKAGE_ARTIFACTS)) {
|
||||
break;
|
||||
}
|
||||
stderr.append(l);
|
||||
stderr.append('\n');
|
||||
}
|
||||
// Load the package artifacts
|
||||
Set<URI> lastUriSet = null;
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null) {
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
if (l.equals(PROTOCOL_PACKAGE_DEPENDENCIES)) {
|
||||
break;
|
||||
}
|
||||
if (l.length() > 1 && l.charAt(0) == '+') {
|
||||
String pkg = l.substring(1);
|
||||
lastUriSet = new HashSet<URI>();
|
||||
packageArtifacts.put(pkg, lastUriSet);
|
||||
} else if (l.length() > 1 && lastUriSet != null) {
|
||||
lastUriSet.add(new URI(l.substring(1)));
|
||||
}
|
||||
}
|
||||
// Load package dependencies
|
||||
Set<String> lastPackageSet = null;
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null) {
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
if (l.equals(PROTOCOL_PACKAGE_PUBLIC_APIS)) {
|
||||
break;
|
||||
}
|
||||
if (l.length() > 1 && l.charAt(0) == '+') {
|
||||
String pkg = l.substring(1);
|
||||
lastPackageSet = new HashSet<String>();
|
||||
packageDependencies.put(pkg, lastPackageSet);
|
||||
} else if (l.length() > 1 && lastPackageSet != null) {
|
||||
lastPackageSet.add(l.substring(1));
|
||||
}
|
||||
}
|
||||
// Load package pubapis
|
||||
Map<String, StringBuffer> tmp = new HashMap<String, StringBuffer>();
|
||||
StringBuffer lastPublicApi = null;
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null) {
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
if (l.equals(PROTOCOL_SYSINFO)) {
|
||||
break;
|
||||
}
|
||||
if (l.length() > 1 && l.charAt(0) == '+') {
|
||||
String pkg = l.substring(1);
|
||||
lastPublicApi = new StringBuffer();
|
||||
tmp.put(pkg, lastPublicApi);
|
||||
} else if (l.length() > 1 && lastPublicApi != null) {
|
||||
lastPublicApi.append(l.substring(1));
|
||||
lastPublicApi.append("\n");
|
||||
}
|
||||
}
|
||||
for (String p : tmp.keySet()) {
|
||||
assert (packagePublicApis.get(p) == null);
|
||||
String api = tmp.get(p).toString();
|
||||
packagePublicApis.put(p, api);
|
||||
}
|
||||
// Now reading the max memory possible.
|
||||
for (;;) {
|
||||
String l = in.readLine();
|
||||
if (l == null) {
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
if (l.equals(PROTOCOL_RETURN_CODE)) {
|
||||
break;
|
||||
}
|
||||
if (l.startsWith("num_cores=") && sysinfo != null) {
|
||||
sysinfo.numCores = Integer.parseInt(l.substring(10));
|
||||
}
|
||||
if (l.startsWith("max_memory=") && sysinfo != null) {
|
||||
sysinfo.maxMemory = Long.parseLong(l.substring(11));
|
||||
}
|
||||
}
|
||||
String l = in.readLine();
|
||||
if (l == null) {
|
||||
err.println("No return value from the server!");
|
||||
return ERROR_FATAL;
|
||||
}
|
||||
rc = Integer.parseInt(l);
|
||||
out.print(stdout);
|
||||
err.print(stderr);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(err);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the server thread until it exits. Either because of inactivity or because the port file has been deleted by someone else, or overtaken by some other
|
||||
* javac server.
|
||||
*/
|
||||
private void run(PortFile portFile, PrintStream err, int keepalive) {
|
||||
boolean fileDeleted = false;
|
||||
long timeSinceLastCompile;
|
||||
try {
|
||||
// Every 5 second (check_portfile_interval) we test if the portfile has disappeared => quit
|
||||
// Or if the last request was finished more than 125 seconds ago => quit
|
||||
// 125 = seconds_of_inactivity_before_shutdown+check_portfile_interval
|
||||
serverSocket.setSoTimeout(CHECK_PORTFILE_INTERVAL*1000);
|
||||
for (;;) {
|
||||
try {
|
||||
Socket s = serverSocket.accept();
|
||||
CompilerThread ct = compilerPool.grabCompilerThread();
|
||||
ct.setSocket(s);
|
||||
compilerPool.execute(ct);
|
||||
flushLog();
|
||||
} catch (java.net.SocketTimeoutException e) {
|
||||
if (compilerPool.numActiveRequests() > 0) {
|
||||
// Never quit while there are active requests!
|
||||
continue;
|
||||
}
|
||||
// If this is the timeout after the portfile
|
||||
// has been deleted by us. Then we truly stop.
|
||||
if (fileDeleted) {
|
||||
log("Quitting because of "+(keepalive+CHECK_PORTFILE_INTERVAL)+" seconds of inactivity!");
|
||||
break;
|
||||
}
|
||||
// Check if the portfile is still there.
|
||||
if (!portFile.exists()) {
|
||||
// Time to quit because the portfile was deleted by another
|
||||
// process, probably by the makefile that is done building.
|
||||
log("Quitting because portfile was deleted!");
|
||||
flushLog();
|
||||
break;
|
||||
}
|
||||
// Check if portfile.stop is still there.
|
||||
if (portFile.markedForStop()) {
|
||||
// Time to quit because another process touched the file
|
||||
// server.port.stop to signal that the server should stop.
|
||||
// This is necessary on some operating systems that lock
|
||||
// the port file hard!
|
||||
log("Quitting because a portfile.stop file was found!");
|
||||
portFile.delete();
|
||||
flushLog();
|
||||
break;
|
||||
}
|
||||
// Does the portfile still point to me?
|
||||
if (!portFile.stillMyValues()) {
|
||||
// Time to quit because another build has started.
|
||||
log("Quitting because portfile is now owned by another javac server!");
|
||||
flushLog();
|
||||
break;
|
||||
}
|
||||
|
||||
// Check how long since the last request finished.
|
||||
long diff = System.currentTimeMillis() - compilerPool.lastRequestFinished();
|
||||
if (diff < keepalive * 1000) {
|
||||
// Do not quit if we have waited less than 120 seconds.
|
||||
continue;
|
||||
}
|
||||
// Ok, time to quit because of inactivity. Perhaps the build
|
||||
// was killed and the portfile not cleaned up properly.
|
||||
portFile.delete();
|
||||
fileDeleted = true;
|
||||
log("" + keepalive + " seconds of inactivity quitting in "
|
||||
+ CHECK_PORTFILE_INTERVAL + " seconds!");
|
||||
flushLog();
|
||||
// Now we have a second 5 second grace
|
||||
// period where javac remote requests
|
||||
// that have loaded the data from the
|
||||
// recently deleted portfile can connect
|
||||
// and complete their requests.
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(err);
|
||||
e.printStackTrace(theLog);
|
||||
flushLog();
|
||||
} finally {
|
||||
compilerPool.shutdown();
|
||||
}
|
||||
long realTime = System.currentTimeMillis() - serverStart;
|
||||
log("Shutting down.");
|
||||
log("Total wall clock time " + realTime + "ms build time " + totalBuildTime + "ms");
|
||||
flushLog();
|
||||
}
|
||||
|
||||
public static void cleanup(String... args) {
|
||||
String settings = Util.findServerSettings(args);
|
||||
if (settings == null) return;
|
||||
String portfile = Util.extractStringOption("portfile", settings);
|
||||
String background = Util.extractStringOption("background", settings);
|
||||
if (background != null && background.equals("false")) {
|
||||
// If the server runs within this jvm, then delete the portfile,
|
||||
// since this jvm is about to exit soon.
|
||||
File f = new File(portfile);
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.server;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.channels.FileLockInterruptionException;
|
||||
import com.sun.tools.sjavac.Log;
|
||||
|
||||
/**
|
||||
* The PortFile class mediates access to a short binary file containing the tcp/ip port (for the localhost)
|
||||
* and a cookie necessary for the server answering on that port. The file can be locked using file system
|
||||
* primitives to avoid race conditions when several javac clients are started at the same. Note that file
|
||||
* system locking is not always supported on a all operating systems and/or file systems.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
class PortFile {
|
||||
|
||||
// Port file format:
|
||||
// byte ordering: high byte first = big endian
|
||||
// Magic nr, 4 byte int, first in file.
|
||||
private final static int magicNr = 0x1174;
|
||||
// Followed by a 4 byte int, with the port nr.
|
||||
// Followed by a 8 byte long, with cookie nr.
|
||||
|
||||
private String filename;
|
||||
private File file;
|
||||
private File stopFile;
|
||||
private RandomAccessFile rwfile;
|
||||
private FileChannel channel;
|
||||
private FileLock lock;
|
||||
|
||||
private boolean containsPortInfo;
|
||||
private int serverPort;
|
||||
private long serverCookie;
|
||||
private int myServerPort;
|
||||
private long myServerCookie;
|
||||
|
||||
/**
|
||||
* Create a new portfile.
|
||||
* @param filename is the path to the file.
|
||||
*/
|
||||
public PortFile(String fn) throws FileNotFoundException
|
||||
{
|
||||
filename = fn;
|
||||
file = new File(filename);
|
||||
stopFile = new File(filename+".stop");
|
||||
rwfile = new RandomAccessFile(file, "rw");
|
||||
// The rwfile should only be readable by the owner of the process
|
||||
// and no other! How do we do that on a RandomAccessFile?
|
||||
channel = rwfile.getChannel();
|
||||
containsPortInfo = false;
|
||||
lock = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the port file.
|
||||
*/
|
||||
void lock() throws IOException {
|
||||
lock = channel.lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the values from the port file in the file system.
|
||||
* Expects the port file to be locked.
|
||||
*/
|
||||
public void getValues() {
|
||||
containsPortInfo = false;
|
||||
if (lock == null) {
|
||||
// Not locked, remain ignorant about port file contents.
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (rwfile.length()>0) {
|
||||
rwfile.seek(0);
|
||||
int nr = rwfile.readInt();
|
||||
serverPort = rwfile.readInt();
|
||||
serverCookie = rwfile.readLong();
|
||||
|
||||
if (nr == magicNr) {
|
||||
containsPortInfo = true;
|
||||
} else {
|
||||
containsPortInfo = false;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
containsPortInfo = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Did the locking and getValues succeed?
|
||||
*/
|
||||
public boolean containsPortInfo() {
|
||||
return containsPortInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* If so, then we can acquire the tcp/ip port on localhost.
|
||||
*/
|
||||
public int getPort() {
|
||||
assert(containsPortInfo);
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* If so, then we can acquire the server cookie.
|
||||
*/
|
||||
public long getCookie() {
|
||||
assert(containsPortInfo);
|
||||
return serverCookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the values into the locked port file.
|
||||
*/
|
||||
public void setValues(int port, long cookie) throws IOException {
|
||||
assert(lock != null);
|
||||
rwfile.seek(0);
|
||||
// Write the magic nr that identifes a port file.
|
||||
rwfile.writeInt(magicNr);
|
||||
rwfile.writeInt(port);
|
||||
rwfile.writeLong(cookie);
|
||||
myServerPort = port;
|
||||
myServerCookie = cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the port file.
|
||||
*/
|
||||
public void delete() throws IOException {
|
||||
// Access to file must be closed before deleting.
|
||||
rwfile.close();
|
||||
// Now delete.
|
||||
file.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the port file still there?
|
||||
*/
|
||||
public boolean exists() throws IOException {
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a stop file there?
|
||||
*/
|
||||
public boolean markedForStop() throws IOException {
|
||||
if (stopFile.exists()) {
|
||||
try {
|
||||
stopFile.delete();
|
||||
} catch (Exception e)
|
||||
{}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock the port file.
|
||||
*/
|
||||
public void unlock() throws IOException {
|
||||
assert(lock != null);
|
||||
lock.release();
|
||||
lock = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the port file to contain values that look valid.
|
||||
* Return true, if a-ok, false if the valid values did not materialize within 5 seconds.
|
||||
*/
|
||||
public synchronized boolean waitForValidValues() throws IOException, FileNotFoundException {
|
||||
for (int tries = 0; tries < 50; tries++) {
|
||||
lock();
|
||||
getValues();
|
||||
unlock();
|
||||
if (containsPortInfo) {
|
||||
Log.debug("Found valid values in port file after waiting "+(tries*100)+"ms");
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e)
|
||||
{}
|
||||
}
|
||||
Log.debug("Gave up waiting for valid values in port file");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the portfile still contains my values, assuming that I am the server.
|
||||
*/
|
||||
public synchronized boolean stillMyValues() throws IOException, FileNotFoundException {
|
||||
for (;;) {
|
||||
try {
|
||||
lock();
|
||||
getValues();
|
||||
unlock();
|
||||
if (containsPortInfo) {
|
||||
if (serverPort == myServerPort &&
|
||||
serverCookie == myServerCookie) {
|
||||
// Everything is ok.
|
||||
return true;
|
||||
}
|
||||
// Someone has overwritten the port file.
|
||||
// Probably another javac server, lets quit.
|
||||
return false;
|
||||
}
|
||||
// Something else is wrong with the portfile. Lets quit.
|
||||
return false;
|
||||
} catch (FileLockInterruptionException e) {
|
||||
continue;
|
||||
}
|
||||
catch (ClosedChannelException e) {
|
||||
// The channel has been closed since sjavac is exiting.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the port file.
|
||||
*/
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A utility class used to report information about the system
|
||||
* where the javac server is running.
|
||||
*
|
||||
* <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></p>
|
||||
*/
|
||||
package com.sun.tools.sjavac.server;
|
||||
|
||||
public class SysInfo {
|
||||
public int numCores;
|
||||
public long maxMemory;
|
||||
|
||||
public SysInfo(int nc, long mm) {
|
||||
numCores = nc;
|
||||
maxMemory = mm;
|
||||
}
|
||||
}
|
590
langtools/test/tools/sjavac/SJavac.java
Normal file
590
langtools/test/tools/sjavac/SJavac.java
Normal file
@ -0,0 +1,590 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test all aspects of sjavac.
|
||||
*
|
||||
* @bug 8004658
|
||||
* @summary Add internal smart javac wrapper to solve JEP 139
|
||||
*
|
||||
* @run main SJavac
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.nio.charset.*;
|
||||
|
||||
import com.sun.tools.sjavac.Main;
|
||||
|
||||
public
|
||||
class SJavac {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
URL url = SJavac.class.getClassLoader().getResource("com/sun/tools/sjavac/Main.class");
|
||||
if (url == null) {
|
||||
// No sjavac in the classpath.
|
||||
System.out.println("pass by default");
|
||||
return;
|
||||
}
|
||||
|
||||
SJavac s = new SJavac();
|
||||
s.test();
|
||||
}
|
||||
|
||||
FileSystem defaultfs = FileSystems.getDefault();
|
||||
|
||||
// Where to put generated sources that will
|
||||
// test aspects of sjavac, ie JTWork/scratch/gensrc
|
||||
Path gensrc;
|
||||
// More gensrc dirs are used to test merging of serveral source roots.
|
||||
Path gensrc2;
|
||||
Path gensrc3;
|
||||
|
||||
// Where to put compiled classes.
|
||||
Path bin;
|
||||
// Where to put c-header files.
|
||||
Path headers;
|
||||
|
||||
// The sjavac compiler.
|
||||
Main main = new Main();
|
||||
|
||||
// Remember the previous bin and headers state here.
|
||||
Map<String,Long> previous_bin_state;
|
||||
Map<String,Long> previous_headers_state;
|
||||
|
||||
public void test() throws Exception {
|
||||
gensrc = defaultfs.getPath("gensrc");
|
||||
gensrc2 = defaultfs.getPath("gensrc2");
|
||||
gensrc3 = defaultfs.getPath("gensrc3");
|
||||
bin = defaultfs.getPath("bin");
|
||||
headers = defaultfs.getPath("headers");
|
||||
|
||||
Files.createDirectory(gensrc);
|
||||
Files.createDirectory(gensrc2);
|
||||
Files.createDirectory(gensrc3);
|
||||
Files.createDirectory(bin);
|
||||
Files.createDirectory(headers);
|
||||
|
||||
initialCompile();
|
||||
incrementalCompileNoChanges();
|
||||
incrementalCompileDroppingClasses();
|
||||
incrementalCompileWithChange();
|
||||
incrementalCompileDropAllNatives();
|
||||
incrementalCompileAddNative();
|
||||
incrementalCompileChangeNative();
|
||||
compileWithOverrideSource();
|
||||
compileWithInvisibleSources();
|
||||
compileCircularSources();
|
||||
|
||||
delete(gensrc);
|
||||
delete(gensrc2);
|
||||
delete(gensrc3);
|
||||
delete(bin);
|
||||
}
|
||||
|
||||
void initialCompile() throws Exception {
|
||||
System.out.println("\nInitial compile of gensrc.");
|
||||
System.out.println("----------------------------");
|
||||
populate(gensrc,
|
||||
"alfa/AINT.java",
|
||||
"package alfa; public interface AINT { void aint(); }",
|
||||
|
||||
"alfa/A.java",
|
||||
"package alfa; public class A implements AINT { "+
|
||||
"public final static int DEFINITION = 17; public void aint() { } }",
|
||||
|
||||
"alfa/AA.java",
|
||||
"package alfa;"+
|
||||
"// A package private class, not contributing to the public api.\n"+
|
||||
"class AA {"+
|
||||
" // A properly nested static inner class.\n"+
|
||||
" static class AAA { }\n"+
|
||||
" // A properly nested inner class.\n"+
|
||||
" class AAAA { }\n"+
|
||||
" Runnable foo() {\n"+
|
||||
" // A proper anonymous class.\n"+
|
||||
" return new Runnable() { public void run() { } };\n"+
|
||||
" }\n"+
|
||||
" AAA aaa;\n"+
|
||||
" AAAA aaaa;\n"+
|
||||
" AAAAA aaaaa;\n"+
|
||||
"}\n"+
|
||||
"class AAAAA {\n"+
|
||||
" // A bad auxiliary class, but no one is referencing it\n"+
|
||||
" // from outside of this source file, therefore it is ok.\n"+
|
||||
"}\n",
|
||||
|
||||
"beta/BINT.java",
|
||||
"package beta;public interface BINT { void foo(); }",
|
||||
|
||||
"beta/B.java",
|
||||
"package beta; import alfa.A; public class B {"+
|
||||
"private int b() { return A.DEFINITION; } native void foo(); }");
|
||||
|
||||
compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false", "--log=debug");
|
||||
previous_bin_state = collectState(bin);
|
||||
previous_headers_state = collectState(headers);
|
||||
}
|
||||
|
||||
void incrementalCompileNoChanges() throws Exception {
|
||||
System.out.println("\nTesting that no change in sources implies no change in binaries.");
|
||||
System.out.println("------------------------------------------------------------------");
|
||||
compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false", "--log=debug");
|
||||
Map<String,Long> new_bin_state = collectState(bin);
|
||||
verifyEqual(new_bin_state, previous_bin_state);
|
||||
Map<String,Long> new_headers_state = collectState(headers);
|
||||
verifyEqual(previous_headers_state, new_headers_state);
|
||||
}
|
||||
|
||||
void incrementalCompileDroppingClasses() throws Exception {
|
||||
System.out.println("\nTesting that deleting AA.java deletes all");
|
||||
System.out.println("generated inner class as well as AA.class");
|
||||
System.out.println("-----------------------------------------");
|
||||
removeFrom(gensrc, "alfa/AA.java");
|
||||
compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false", "--log=debug");
|
||||
Map<String,Long> new_bin_state = collectState(bin);
|
||||
verifyThatFilesHaveBeenRemoved(previous_bin_state, new_bin_state,
|
||||
"bin/alfa/AA$1.class",
|
||||
"bin/alfa/AA$AAAA.class",
|
||||
"bin/alfa/AA$AAA.class",
|
||||
"bin/alfa/AAAAA.class",
|
||||
"bin/alfa/AA.class");
|
||||
|
||||
previous_bin_state = new_bin_state;
|
||||
Map<String,Long> new_headers_state = collectState(headers);
|
||||
verifyEqual(previous_headers_state, new_headers_state);
|
||||
}
|
||||
|
||||
void incrementalCompileWithChange() throws Exception {
|
||||
System.out.println("\nNow update the A.java file with a new timestamps and");
|
||||
System.out.println("new final static definition. This should trigger a recompile,");
|
||||
System.out.println("not only of alfa, but also beta.");
|
||||
System.out.println("But check that the generated native header was not updated!");
|
||||
System.out.println("Since we did not modify the native api of B.");
|
||||
System.out.println("-------------------------------------------------------------");
|
||||
|
||||
populate(gensrc,"alfa/A.java",
|
||||
"package alfa; public class A implements AINT { "+
|
||||
"public final static int DEFINITION = 18; public void aint() { } private void foo() { } }");
|
||||
|
||||
compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false", "--log=debug");
|
||||
Map<String,Long> new_bin_state = collectState(bin);
|
||||
|
||||
verifyNewerFiles(previous_bin_state, new_bin_state,
|
||||
"bin/alfa/A.class",
|
||||
"bin/alfa/AINT.class",
|
||||
"bin/beta/B.class",
|
||||
"bin/beta/BINT.class",
|
||||
"bin/javac_state");
|
||||
previous_bin_state = new_bin_state;
|
||||
|
||||
Map<String,Long> new_headers_state = collectState(headers);
|
||||
verifyEqual(new_headers_state, previous_headers_state);
|
||||
}
|
||||
|
||||
void incrementalCompileDropAllNatives() throws Exception {
|
||||
System.out.println("\nNow update the B.java file with one less native method,");
|
||||
System.out.println("ie it has no longer any methods!");
|
||||
System.out.println("Verify that beta_B.h is removed!");
|
||||
System.out.println("---------------------------------------------------------");
|
||||
|
||||
populate(gensrc,"beta/B.java",
|
||||
"package beta; import alfa.A; public class B {"+
|
||||
"private int b() { return A.DEFINITION; } }");
|
||||
|
||||
compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false", "--log=debug");
|
||||
Map<String,Long> new_bin_state = collectState(bin);
|
||||
verifyNewerFiles(previous_bin_state, new_bin_state,
|
||||
"bin/beta/B.class",
|
||||
"bin/beta/BINT.class",
|
||||
"bin/javac_state");
|
||||
previous_bin_state = new_bin_state;
|
||||
|
||||
Map<String,Long> new_headers_state = collectState(headers);
|
||||
verifyThatFilesHaveBeenRemoved(previous_headers_state, new_headers_state,
|
||||
"headers/beta_B.h");
|
||||
previous_headers_state = new_headers_state;
|
||||
}
|
||||
|
||||
void incrementalCompileAddNative() throws Exception {
|
||||
System.out.println("\nNow update the B.java file with a final static annotated with @Native.");
|
||||
System.out.println("Verify that beta_B.h is added again!");
|
||||
System.out.println("------------------------------------------------------------------------");
|
||||
|
||||
populate(gensrc,"beta/B.java",
|
||||
"package beta; import alfa.A; public class B {"+
|
||||
"private int b() { return A.DEFINITION; } "+
|
||||
"@java.lang.annotation.Native final static int alfa = 42; }");
|
||||
|
||||
compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false", "--log=debug");
|
||||
Map<String,Long> new_bin_state = collectState(bin);
|
||||
verifyNewerFiles(previous_bin_state, new_bin_state,
|
||||
"bin/beta/B.class",
|
||||
"bin/beta/BINT.class",
|
||||
"bin/javac_state");
|
||||
previous_bin_state = new_bin_state;
|
||||
|
||||
Map<String,Long> new_headers_state = collectState(headers);
|
||||
verifyThatFilesHaveBeenAdded(previous_headers_state, new_headers_state,
|
||||
"headers/beta_B.h");
|
||||
previous_headers_state = new_headers_state;
|
||||
}
|
||||
|
||||
void incrementalCompileChangeNative() throws Exception {
|
||||
System.out.println("\nNow update the B.java file with a new value for the final static"+
|
||||
" annotated with @Native.");
|
||||
System.out.println("Verify that beta_B.h is rewritten again!");
|
||||
System.out.println("-------------------------------------------------------------------");
|
||||
|
||||
populate(gensrc,"beta/B.java",
|
||||
"package beta; import alfa.A; public class B {"+
|
||||
"private int b() { return A.DEFINITION; } "+
|
||||
"@java.lang.annotation.Native final static int alfa = 43; }");
|
||||
|
||||
compile("gensrc", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false", "--log=debug");
|
||||
Map<String,Long> new_bin_state = collectState(bin);
|
||||
verifyNewerFiles(previous_bin_state, new_bin_state,
|
||||
"bin/beta/B.class",
|
||||
"bin/beta/BINT.class",
|
||||
"bin/javac_state");
|
||||
previous_bin_state = new_bin_state;
|
||||
|
||||
Map<String,Long> new_headers_state = collectState(headers);
|
||||
verifyNewerFiles(previous_headers_state, new_headers_state,
|
||||
"headers/beta_B.h");
|
||||
previous_headers_state = new_headers_state;
|
||||
}
|
||||
|
||||
void compileWithOverrideSource() throws Exception {
|
||||
System.out.println("\nNow verify that we can override sources to be compiled.");
|
||||
System.out.println("Compile gensrc and gensrc2. However do not compile broken beta.B in gensrc,");
|
||||
System.out.println("only compile ok beta.B in gensrc2.");
|
||||
System.out.println("---------------------------------------------------------------------------");
|
||||
|
||||
delete(gensrc);
|
||||
delete(gensrc2);
|
||||
delete(bin);
|
||||
previous_bin_state = collectState(bin);
|
||||
|
||||
populate(gensrc,"alfa/A.java",
|
||||
"package alfa; import beta.B; import gamma.C; public class A { B b; C c; }",
|
||||
"beta/B.java",
|
||||
"package beta; public class B { broken",
|
||||
"gamma/C.java",
|
||||
"package gamma; public class C { }");
|
||||
|
||||
populate(gensrc2,
|
||||
"beta/B.java",
|
||||
"package beta; public class B { }");
|
||||
|
||||
compile("-x", "beta", "gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false");
|
||||
Map<String,Long> new_bin_state = collectState(bin);
|
||||
verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
|
||||
"bin/alfa/A.class",
|
||||
"bin/beta/B.class",
|
||||
"bin/gamma/C.class",
|
||||
"bin/javac_state");
|
||||
|
||||
System.out.println("----- Compile with exluded beta went well!");
|
||||
delete(bin);
|
||||
compileExpectFailure("gensrc", "gensrc2", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false");
|
||||
|
||||
System.out.println("----- Compile without exluded beta failed, as expected! Good!");
|
||||
delete(bin);
|
||||
}
|
||||
|
||||
void compileWithInvisibleSources() throws Exception {
|
||||
System.out.println("\nNow verify that we can make sources invisible to linking (sourcepath).");
|
||||
System.out.println("Compile gensrc and link against gensrc2 and gensrc3, however");
|
||||
System.out.println("gensrc2 contains broken code in beta.B, thus we must exclude that package");
|
||||
System.out.println("fortunately gensrc3 contains a proper beta.B.");
|
||||
System.out.println("------------------------------------------------------------------------");
|
||||
|
||||
// Start with a fresh gensrcs and bin.
|
||||
delete(gensrc);
|
||||
delete(gensrc2);
|
||||
delete(gensrc3);
|
||||
delete(bin);
|
||||
previous_bin_state = collectState(bin);
|
||||
|
||||
populate(gensrc,"alfa/A.java",
|
||||
"package alfa; import beta.B; import gamma.C; public class A { B b; C c; }");
|
||||
populate(gensrc2,"beta/B.java",
|
||||
"package beta; public class B { broken",
|
||||
"gamma/C.java",
|
||||
"package gamma; public class C { }");
|
||||
populate(gensrc3, "beta/B.java",
|
||||
"package beta; public class B { }");
|
||||
|
||||
compile("gensrc", "-x", "beta", "-sourcepath", "gensrc2",
|
||||
"-sourcepath", "gensrc3", "-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false");
|
||||
|
||||
System.out.println("The first compile went well!");
|
||||
Map<String,Long> new_bin_state = collectState(bin);
|
||||
verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
|
||||
"bin/alfa/A.class",
|
||||
"bin/javac_state");
|
||||
|
||||
System.out.println("----- Compile with exluded beta went well!");
|
||||
delete(bin);
|
||||
compileExpectFailure("gensrc", "-sourcepath", "gensrc2", "-sourcepath", "gensrc3",
|
||||
"-d", "bin", "-h", "headers", "-j", "1",
|
||||
"--server:portfile=testserver,background=false");
|
||||
|
||||
System.out.println("----- Compile without exluded beta failed, as expected! Good!");
|
||||
delete(bin);
|
||||
}
|
||||
|
||||
void compileCircularSources() throws Exception {
|
||||
System.out.println("\nNow verify that circular sources split on multiple cores can be compiled.");
|
||||
System.out.println("---------------------------------------------------------------------------");
|
||||
|
||||
// Start with a fresh gensrcs and bin.
|
||||
delete(gensrc);
|
||||
delete(gensrc2);
|
||||
delete(gensrc3);
|
||||
delete(bin);
|
||||
previous_bin_state = collectState(bin);
|
||||
|
||||
populate(gensrc,"alfa/A.java",
|
||||
"package alfa; public class A { beta.B b; }",
|
||||
"beta/B.java",
|
||||
"package beta; public class B { gamma.C c; }",
|
||||
"gamma/C.java",
|
||||
"package gamma; public class C { alfa.A a; }");
|
||||
|
||||
compile("gensrc", "-d", "bin", "-h", "headers", "-j", "3",
|
||||
"--server:portfile=testserver,background=false","--log=debug");
|
||||
Map<String,Long> new_bin_state = collectState(bin);
|
||||
verifyThatFilesHaveBeenAdded(previous_bin_state, new_bin_state,
|
||||
"bin/alfa/A.class",
|
||||
"bin/beta/B.class",
|
||||
"bin/gamma/C.class",
|
||||
"bin/javac_state");
|
||||
delete(bin);
|
||||
}
|
||||
|
||||
void removeFrom(Path dir, String... args) throws IOException {
|
||||
for (String filename : args) {
|
||||
Path p = dir.resolve(filename);
|
||||
Files.delete(p);
|
||||
}
|
||||
}
|
||||
|
||||
void populate(Path src, String... args) throws IOException {
|
||||
if (!Files.exists(src)) {
|
||||
Files.createDirectory(src);
|
||||
}
|
||||
String[] a = args;
|
||||
for (int i = 0; i<a.length; i+=2) {
|
||||
String filename = a[i];
|
||||
String content = a[i+1];
|
||||
Path p = src.resolve(filename);
|
||||
Files.createDirectories(p.getParent());
|
||||
PrintWriter out = new PrintWriter(Files.newBufferedWriter(p,
|
||||
Charset.defaultCharset()));
|
||||
out.println(content);
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
void delete(Path root) throws IOException {
|
||||
if (!Files.exists(root)) return;
|
||||
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
|
||||
{
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException
|
||||
{
|
||||
if (e == null) {
|
||||
if (!dir.equals(root)) Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
} else {
|
||||
// directory iteration failed
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void compile(String... args) throws Exception {
|
||||
int rc = main.go(args, System.out, System.err);
|
||||
if (rc != 0) throw new Exception("Error during compile!");
|
||||
|
||||
// Wait a second, to get around the (temporary) problem with
|
||||
// second resolution in the Java file api. But do not do this
|
||||
// on windows where the timestamps work.
|
||||
long in_a_sec = System.currentTimeMillis()+1000;
|
||||
while (in_a_sec > System.currentTimeMillis()) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void compileExpectFailure(String... args) throws Exception {
|
||||
int rc = main.go(args, System.out, System.err);
|
||||
if (rc == 0) throw new Exception("Expected error during compile! Did not fail!");
|
||||
}
|
||||
|
||||
Map<String,Long> collectState(Path dir) throws IOException
|
||||
{
|
||||
final Map<String,Long> files = new HashMap<>();
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
throws IOException
|
||||
{
|
||||
files.put(file.toString(),new Long(Files.getLastModifiedTime(file).toMillis()));
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
return files;
|
||||
}
|
||||
|
||||
void verifyThatFilesHaveBeenRemoved(Map<String,Long> from,
|
||||
Map<String,Long> to,
|
||||
String... args) throws Exception {
|
||||
|
||||
Set<String> froms = from.keySet();
|
||||
Set<String> tos = to.keySet();
|
||||
|
||||
if (froms.equals(tos)) {
|
||||
throw new Exception("Expected new state to have fewer files than previous state!");
|
||||
}
|
||||
|
||||
for (String t : tos) {
|
||||
if (!froms.contains(t)) {
|
||||
throw new Exception("Expected "+t+" to exist in previous state!");
|
||||
}
|
||||
}
|
||||
|
||||
for (String f : args) {
|
||||
f = f.replace("/", File.separator);
|
||||
if (!froms.contains(f)) {
|
||||
throw new Exception("Expected "+f+" to exist in previous state!");
|
||||
}
|
||||
if (tos.contains(f)) {
|
||||
throw new Exception("Expected "+f+" to have been removed from the new state!");
|
||||
}
|
||||
}
|
||||
|
||||
if (froms.size() - args.length != tos.size()) {
|
||||
throw new Exception("There are more removed files than the expected list!");
|
||||
}
|
||||
}
|
||||
|
||||
void verifyThatFilesHaveBeenAdded(Map<String,Long> from,
|
||||
Map<String,Long> to,
|
||||
String... args) throws Exception {
|
||||
|
||||
Set<String> froms = from.keySet();
|
||||
Set<String> tos = to.keySet();
|
||||
|
||||
if (froms.equals(tos)) {
|
||||
throw new Exception("Expected new state to have more files than previous state!");
|
||||
}
|
||||
|
||||
for (String t : froms) {
|
||||
if (!tos.contains(t)) {
|
||||
throw new Exception("Expected "+t+" to exist in new state!");
|
||||
}
|
||||
}
|
||||
|
||||
for (String f : args) {
|
||||
f = f.replace("/", File.separator);
|
||||
if (!tos.contains(f)) {
|
||||
throw new Exception("Expected "+f+" to have been added to new state!");
|
||||
}
|
||||
if (froms.contains(f)) {
|
||||
throw new Exception("Expected "+f+" to not exist in previous state!");
|
||||
}
|
||||
}
|
||||
|
||||
if (froms.size() + args.length != tos.size()) {
|
||||
throw new Exception("There are more added files than the expected list!");
|
||||
}
|
||||
}
|
||||
|
||||
void verifyNewerFiles(Map<String,Long> from,
|
||||
Map<String,Long> to,
|
||||
String... args) throws Exception {
|
||||
if (!from.keySet().equals(to.keySet())) {
|
||||
throw new Exception("Expected the set of files to be identical!");
|
||||
}
|
||||
Set<String> files = new HashSet<String>();
|
||||
for (String s : args) {
|
||||
files.add(s.replace("/", File.separator));
|
||||
}
|
||||
for (String fn : from.keySet()) {
|
||||
long f = from.get(fn);
|
||||
long t = to.get(fn);
|
||||
if (files.contains(fn)) {
|
||||
if (t <= f) {
|
||||
throw new Exception("Expected "+fn+" to have a more recent timestamp!");
|
||||
}
|
||||
} else {
|
||||
if (t != f) {
|
||||
throw new Exception("Expected "+fn+" to have the same timestamp!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String print(Map<String,Long> m) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
Set<String> keys = m.keySet();
|
||||
for (String k : keys) {
|
||||
b.append(k+" "+m.get(k)+"\n");
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
void verifyEqual(Map<String,Long> from, Map<String,Long> to) throws Exception {
|
||||
if (!from.equals(to)) {
|
||||
System.out.println("FROM---"+print(from));
|
||||
System.out.println("TO-----"+print(to));
|
||||
throw new Exception("The dir should not differ! But it does!");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user