8182285: Speeding up incremental build by hashing module APIs
Co-authored-by: Jan Lahoda <jan.lahoda@oracle.com> Reviewed-by: ihse
This commit is contained in:
parent
5e00c162bd
commit
a6aad28019
@ -55,7 +55,18 @@ $(eval $(call SetupJavaCompilation, jdk.scripting.nashorn, \
|
||||
MODULE := jdk.scripting.nashorn, \
|
||||
SRC := $(TOPDIR)/src/jdk.scripting.nashorn/share/classes, \
|
||||
COPY := .properties .js, \
|
||||
BIN := $(SUPPORT_OUTPUTDIR)/special_classes))
|
||||
BIN := $(SUPPORT_OUTPUTDIR)/special_classes, \
|
||||
CREATE_API_DIGEST := true, \
|
||||
))
|
||||
|
||||
# Declare dependencies between java compilations of different modules.
|
||||
# Since the other modules are declared in different invocations of this file,
|
||||
# use the macro to find the correct target file to depend on.
|
||||
# Only the javac compilation actually depends on other modules so limit
|
||||
# dependency declaration to that by using the *_COMPILE_TARGET variable.
|
||||
$(jdk.scripting.nashorn_COMPILE_TARGET): $(foreach d, $(call FindDepsForModule, jdk.scripting.nashorn), \
|
||||
$(call SetupJavaCompilationApiTarget, $d, \
|
||||
$(if $($d_BIN), $($d_BIN), $(JDK_OUTPUTDIR)/modules/$d)))
|
||||
|
||||
NASGEN_SRC := $(TOPDIR)/make/nashorn/buildtools/nasgen/src
|
||||
ASM_SRC := $(TOPDIR)/src/java.base/share/classes/jdk/internal/org/objectweb/asm
|
||||
|
@ -630,6 +630,7 @@ $(eval $(call SetupJavaCompilation, $(MODULE), \
|
||||
FAIL_NO_SRC := $(FAIL_NO_SRC), \
|
||||
BIN := $(if $($(MODULE)_BIN), $($(MODULE)_BIN), $(JDK_OUTPUTDIR)/modules), \
|
||||
HEADERS := $(SUPPORT_OUTPUTDIR)/headers, \
|
||||
CREATE_API_DIGEST := true, \
|
||||
ADD_JAVAC_FLAGS := \
|
||||
$($(MODULE)_ADD_JAVAC_FLAGS) \
|
||||
--module-source-path $(MODULESOURCEPATH) \
|
||||
@ -645,7 +646,7 @@ TARGETS += $($(MODULE)) $($(MODULE)_COPY_EXTRA)
|
||||
# Only the javac compilation actually depends on other modules so limit
|
||||
# dependency declaration to that by using the *_COMPILE_TARGET variable.
|
||||
$($(MODULE)_COMPILE_TARGET): $(foreach d, $(call FindDepsForModule, $(MODULE)), \
|
||||
$(call SetupJavaCompilationCompileTarget, $d, \
|
||||
$(call SetupJavaCompilationApiTarget, $d, \
|
||||
$(if $($d_BIN), $($d_BIN), $(JDK_OUTPUTDIR)/modules/$d)))
|
||||
|
||||
################################################################################
|
||||
|
@ -42,13 +42,14 @@ BUILD_TOOLS_SRC_DIRS += \
|
||||
$(BUILDTOOLS_OUTPUTDIR)/interim_cldrconverter_classes \
|
||||
#
|
||||
|
||||
$(eval $(call SetupJavaCompilation, BUILD_TOOLS_JDK, \
|
||||
$(eval $(call SetupJavaCompilation,BUILD_TOOLS_JDK, \
|
||||
SETUP := GENERATE_OLDBYTECODE, \
|
||||
SRC := $(BUILD_TOOLS_SRC_DIRS), \
|
||||
EXCLUDES := \
|
||||
build/tools/deps \
|
||||
build/tools/docs \
|
||||
build/tools/jigsaw \
|
||||
build/tools/depend \
|
||||
, \
|
||||
BIN := $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes, \
|
||||
ADD_JAVAC_FLAGS := \
|
||||
@ -70,4 +71,21 @@ TARGETS += $(COPY_NIMBUS_TEMPLATES)
|
||||
|
||||
################################################################################
|
||||
|
||||
$(eval $(call SetupJavaCompilation, COMPILE_DEPEND, \
|
||||
SETUP := GENERATE_OLDBYTECODE, \
|
||||
SRC := $(TOPDIR)/make/jdk/src/classes, \
|
||||
INCLUDES := build/tools/depend, \
|
||||
BIN := $(BUILDTOOLS_OUTPUTDIR)/depend, \
|
||||
))
|
||||
|
||||
DEPEND_SERVICE_PROVIDER := $(BUILDTOOLS_OUTPUTDIR)/depend/META-INF/services/com.sun.source.util.Plugin
|
||||
|
||||
$(DEPEND_SERVICE_PROVIDER):
|
||||
$(call MakeDir, $(BUILDTOOLS_OUTPUTDIR)/depend/META-INF/services)
|
||||
$(ECHO) build.tools.depend.Depend > $@
|
||||
|
||||
TARGETS += $(COMPILE_DEPEND) $(DEPEND_SERVICE_PROVIDER)
|
||||
|
||||
################################################################################
|
||||
|
||||
all: $(TARGETS)
|
||||
|
@ -147,6 +147,8 @@ endef
|
||||
# Parameter 1 is the name of the rule. This name is used as variable prefix,
|
||||
# and the targets generated are listed in a variable by that name.
|
||||
#
|
||||
# The target for public API digest is returned in $1_API_TARGET.
|
||||
#
|
||||
# Remaining parameters are named arguments. These include:
|
||||
# SETUP:=must point to a previously setup java compiler, for example: SETUP:=BOOTJAVAC
|
||||
# JVM:=path to ..bin/java
|
||||
@ -175,6 +177,9 @@ endef
|
||||
# FAIL_NO_SRC:=Set to false to not fail the build if no source files are found,
|
||||
# default is true.
|
||||
# DEBUG_SYMBOLS:=Set to false to disable generation of debug symbols.
|
||||
# CREATE_API_DIGEST:=Set to true to use a javac plugin to generate a public API
|
||||
# hash which can be used for down stream dependencies to only rebuild
|
||||
# when the API changes. Implicitly used in sjavac.
|
||||
SetupJavaCompilation = $(NamedParamsMacroTemplate)
|
||||
define SetupJavaCompilationBody
|
||||
|
||||
@ -327,6 +332,7 @@ define SetupJavaCompilationBody
|
||||
$$(SPACE),%20,$$(subst $$(COMMA),%2C,$$(strip $$($1_SERVER_JVM) $$($1_SJAVAC))))
|
||||
|
||||
$1_COMPILE_TARGET := $$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$1_batch
|
||||
$1_API_TARGET := $$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$1_pubapi
|
||||
|
||||
ifeq ($$($1_DISABLE_SJAVAC)x$$(ENABLE_SJAVAC),xyes)
|
||||
# Using sjavac to compile.
|
||||
@ -386,13 +392,11 @@ define SetupJavaCompilationBody
|
||||
# Create a pubapi file that only changes when the pubapi changes. Dependent
|
||||
# compilations can use this file to only get recompiled when pubapi has changed.
|
||||
# Grep returns 1 if no matching lines are found. Do not fail for this.
|
||||
$(GREP) -e "^I" $$($1_BIN)$$($1_MODULE_SUBDIR)/javac_state > $$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$1_pubapi.tmp \
|
||||
|| test "$$$$?" = "1"
|
||||
if [ ! -f $$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$1_pubapi ] \
|
||||
|| [ "`$(DIFF) $$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$1_pubapi \
|
||||
$$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$1_pubapi.tmp`" != "" ]; then \
|
||||
$(MV) $$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$1_pubapi.tmp \
|
||||
$$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$1_pubapi; \
|
||||
$(GREP) -e "^I" $$($1_BIN)$$($1_MODULE_SUBDIR)/javac_state \
|
||||
> $$($1_API_TARGET).tmp || test "$$$$?" = "1"
|
||||
if [ ! -f $$($1_API_TARGET) ] \
|
||||
|| [ "`$(DIFF) $$($1_API_TARGET) $$($1_API_TARGET).tmp`" != "" ]; then \
|
||||
$(MV) $$($1_API_TARGET).tmp $$($1_API_TARGET); \
|
||||
fi
|
||||
|
||||
else
|
||||
@ -432,15 +436,26 @@ define SetupJavaCompilationBody
|
||||
$1_JAVAC_CMD := $$($1_JAVAC)
|
||||
endif
|
||||
|
||||
ifeq ($$($1_CREATE_API_DIGEST), true)
|
||||
$1_API_DIGEST_FLAGS := \
|
||||
-classpath $(BUILDTOOLS_OUTPUTDIR)/depend \
|
||||
-Xplugin:"depend $$($1_API_TARGET)" \
|
||||
#
|
||||
|
||||
$1_EXTRA_DEPS := $(BUILDTOOLS_OUTPUTDIR)/depend/_the.COMPILE_DEPEND_batch
|
||||
endif
|
||||
|
||||
# When not using sjavac, pass along all sources to javac using an @file.
|
||||
$$($1_COMPILE_TARGET): $$($1_SRCS) $$($1_DEPENDS) $$($1_VARDEPS_FILE)
|
||||
$$($1_COMPILE_TARGET): $$($1_SRCS) $$($1_DEPENDS) $$($1_VARDEPS_FILE) \
|
||||
$$($1_EXTRA_DEPS)
|
||||
$$(call MakeDir, $$(@D))
|
||||
$$(eval $$(call ListPathsSafely,$1_SRCS, $$@.tmp))
|
||||
$$(call LogWarn, Compiling $$(words $$($1_SRCS)) files for $1)
|
||||
$$(call ExecuteWithLog, $$($1_BIN)$$($1_MODULE_SUBDIR)/_the.$$($1_SAFE_NAME)_batch, \
|
||||
$$($1_JVM) $$($1_JAVAC_CMD) $$($1_FLAGS) \
|
||||
-implicit:none \
|
||||
-d $$($1_BIN) $$($1_HEADERS_ARG) @$$@.tmp) && \
|
||||
$$($1_API_DIGEST_FLAGS) \
|
||||
-d $$($1_BIN) $$($1_HEADERS_ARG) @$$@.tmp) && \
|
||||
$(MV) $$@.tmp $$@
|
||||
endif
|
||||
|
||||
@ -497,9 +512,10 @@ endef
|
||||
# to declare and evaluate it again.
|
||||
# param 1 is for example BUILD_MYPACKAGE
|
||||
# param 2 is the output directory (BIN)
|
||||
define SetupJavaCompilationCompileTarget
|
||||
$(if $(findstring yes, $(ENABLE_SJAVAC)), $(strip $2)/_the.$(strip $1)_pubapi, \
|
||||
$(strip $2)/_the.$(strip $1)_batch)
|
||||
endef
|
||||
SetupJavaCompilationCompileTarget = \
|
||||
$(strip $2)/_the.$(strip $1)_batch
|
||||
|
||||
SetupJavaCompilationApiTarget = \
|
||||
$(strip $2)/_the.$(strip $1)_pubapi
|
||||
|
||||
endif # _JAVA_COMPILATION_GMK
|
||||
|
537
make/jdk/src/classes/build/tools/depend/Depend.java
Normal file
537
make/jdk/src/classes/build/tools/depend/Depend.java
Normal file
@ -0,0 +1,537 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 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 build.tools.depend;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.AnnotationValueVisitor;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementVisitor;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.ModuleElement;
|
||||
import javax.lang.model.element.ModuleElement.DirectiveVisitor;
|
||||
import javax.lang.model.element.ModuleElement.ExportsDirective;
|
||||
import javax.lang.model.element.ModuleElement.OpensDirective;
|
||||
import javax.lang.model.element.ModuleElement.ProvidesDirective;
|
||||
import javax.lang.model.element.ModuleElement.RequiresDirective;
|
||||
import javax.lang.model.element.ModuleElement.UsesDirective;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.QualifiedNameable;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.TypeParameterElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.ErrorType;
|
||||
import javax.lang.model.type.ExecutableType;
|
||||
import javax.lang.model.type.IntersectionType;
|
||||
import javax.lang.model.type.NoType;
|
||||
import javax.lang.model.type.NullType;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.type.TypeVariable;
|
||||
import javax.lang.model.type.TypeVisitor;
|
||||
import javax.lang.model.type.UnionType;
|
||||
import javax.lang.model.type.WildcardType;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
import javax.tools.JavaFileObject;
|
||||
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.Plugin;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import com.sun.source.util.TaskEvent.Kind;
|
||||
import com.sun.source.util.TaskListener;
|
||||
import com.sun.source.util.TreePath;
|
||||
import com.sun.source.util.Trees;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
|
||||
public class Depend implements Plugin {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "depend";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(JavacTask jt, String... args) {
|
||||
jt.addTaskListener(new TaskListener() {
|
||||
private final Map<ModuleElement, Set<PackageElement>> apiPackages = new HashMap<>();
|
||||
private final MessageDigest apiHash;
|
||||
{
|
||||
try {
|
||||
apiHash = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void started(TaskEvent te) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finished(TaskEvent te) {
|
||||
if (te.getKind() == Kind.ANALYZE) {
|
||||
if (te.getSourceFile().isNameCompatible("module-info", JavaFileObject.Kind.SOURCE)) {
|
||||
ModuleElement mod = (ModuleElement) Trees.instance(jt).getElement(new TreePath(te.getCompilationUnit()));
|
||||
new APIVisitor(apiHash).visit(mod);
|
||||
} else if (te.getSourceFile().isNameCompatible("package-info", JavaFileObject.Kind.SOURCE)) {
|
||||
//ignore - cannot contain important changes (?)
|
||||
} else {
|
||||
TypeElement clazz = te.getTypeElement();
|
||||
ModuleElement mod = jt.getElements().getModuleOf(clazz);
|
||||
Set<PackageElement> thisModulePackages = apiPackages.computeIfAbsent(mod, m -> {
|
||||
return ElementFilter.exportsIn(mod.getDirectives())
|
||||
.stream()
|
||||
.map(ed -> ed.getPackage())
|
||||
.collect(Collectors.toSet());
|
||||
});
|
||||
if (thisModulePackages.contains(jt.getElements().getPackageOf(clazz))) {
|
||||
new APIVisitor(apiHash).visit(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (te.getKind() == Kind.COMPILATION) {
|
||||
String previousSignature = null;
|
||||
File digestFile = new File(args[0]);
|
||||
try (InputStream in = new FileInputStream(digestFile)) {
|
||||
previousSignature = new String(in.readAllBytes(), "UTF-8");
|
||||
} catch (IOException ex) {
|
||||
//ignore
|
||||
}
|
||||
String currentSignature = Depend.this.toString(apiHash.digest());
|
||||
if (!Objects.equals(previousSignature, currentSignature)) {
|
||||
digestFile.getParentFile().mkdirs();
|
||||
try (OutputStream out = new FileOutputStream(digestFile)) {
|
||||
out.write(currentSignature.getBytes("UTF-8"));
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String toString(byte[] digest) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
for (byte b : digest) {
|
||||
result.append(String.format("%X", b));
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static final class APIVisitor implements ElementVisitor<Void, Void>,
|
||||
TypeVisitor<Void, Void>,
|
||||
AnnotationValueVisitor<Void, Void>,
|
||||
DirectiveVisitor<Void, Void> {
|
||||
|
||||
private final MessageDigest apiHash;
|
||||
private final Charset utf8;
|
||||
|
||||
public APIVisitor(MessageDigest apiHash) {
|
||||
this.apiHash = apiHash;
|
||||
utf8 = Charset.forName("UTF-8");
|
||||
}
|
||||
|
||||
public Void visit(Iterable<? extends Element> list, Void p) {
|
||||
list.forEach(e -> visit(e, p));
|
||||
return null;
|
||||
}
|
||||
|
||||
private void update(CharSequence data) {
|
||||
apiHash.update(data.toString().getBytes(utf8));
|
||||
}
|
||||
|
||||
private void visit(Iterable<? extends TypeMirror> types) {
|
||||
for (TypeMirror type : types) {
|
||||
visit(type);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAnnotation(AnnotationMirror am) {
|
||||
update("@");
|
||||
visit(am.getAnnotationType());
|
||||
am.getElementValues()
|
||||
.keySet()
|
||||
.stream()
|
||||
.sorted((ee1, ee2) -> ee1.getSimpleName().toString().compareTo(ee2.getSimpleName().toString()))
|
||||
.forEach(ee -> {
|
||||
visit(ee);
|
||||
visit(am.getElementValues().get(ee));
|
||||
});
|
||||
}
|
||||
|
||||
private void updateAnnotations(Iterable<? extends AnnotationMirror> annotations) {
|
||||
for (AnnotationMirror am : annotations) {
|
||||
if (am.getAnnotationType().asElement().getAnnotation(Documented.class) == null)
|
||||
continue;
|
||||
updateAnnotation(am);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(Element e, Void p) {
|
||||
if (e.getKind() != ElementKind.MODULE &&
|
||||
!e.getModifiers().contains(Modifier.PUBLIC) &&
|
||||
!e.getModifiers().contains(Modifier.PROTECTED)) {
|
||||
return null;
|
||||
}
|
||||
update(e.getKind().name());
|
||||
update(e.getModifiers().stream()
|
||||
.map(mod -> mod.name())
|
||||
.collect(Collectors.joining(",", "[", "]")));
|
||||
update(e.getSimpleName());
|
||||
updateAnnotations(e.getAnnotationMirrors());
|
||||
return e.accept(this, p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(Element e) {
|
||||
return visit(e, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitModule(ModuleElement e, Void p) {
|
||||
update(String.valueOf(e.isOpen()));
|
||||
update(e.getQualifiedName());
|
||||
e.getDirectives()
|
||||
.stream()
|
||||
.forEach(d -> d.accept(this, null));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPackage(PackageElement e, Void p) {
|
||||
throw new UnsupportedOperationException("Should not get here.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitType(TypeElement e, Void p) {
|
||||
visit(e.getTypeParameters(), p);
|
||||
visit(e.getSuperclass());
|
||||
visit(e.getInterfaces());
|
||||
visit(e.getEnclosedElements(), p);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitVariable(VariableElement e, Void p) {
|
||||
visit(e.asType());
|
||||
update(String.valueOf(e.getConstantValue()));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitExecutable(ExecutableElement e, Void p) {
|
||||
update("<");
|
||||
visit(e.getTypeParameters(), p);
|
||||
update(">");
|
||||
visit(e.getReturnType());
|
||||
update("(");
|
||||
visit(e.getParameters(), p);
|
||||
update(")");
|
||||
visit(e.getThrownTypes());
|
||||
update(String.valueOf(e.getDefaultValue()));
|
||||
update(String.valueOf(e.isVarArgs()));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitTypeParameter(TypeParameterElement e, Void p) {
|
||||
visit(e.getBounds());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitUnknown(Element e, Void p) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(TypeMirror t, Void p) {
|
||||
if (t == null) {
|
||||
update("null");
|
||||
return null;
|
||||
}
|
||||
update(t.getKind().name());
|
||||
updateAnnotations(t.getAnnotationMirrors());
|
||||
t.accept(this, p);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPrimitive(PrimitiveType t, Void p) {
|
||||
return null; //done
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitNull(NullType t, Void p) {
|
||||
return null; //done
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitArray(ArrayType t, Void p) {
|
||||
visit(t.getComponentType());
|
||||
update("[]");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitDeclared(DeclaredType t, Void p) {
|
||||
update(((QualifiedNameable) t.asElement()).getQualifiedName());
|
||||
update("<");
|
||||
visit(t.getTypeArguments());
|
||||
update(">");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitError(ErrorType t, Void p) {
|
||||
return visitDeclared(t, p);
|
||||
}
|
||||
|
||||
private final Set<Element> seenVariables = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public Void visitTypeVariable(TypeVariable t, Void p) {
|
||||
Element decl = t.asElement();
|
||||
if (!seenVariables.add(decl)) {
|
||||
return null;
|
||||
}
|
||||
visit(decl, null);
|
||||
visit(t.getLowerBound(), null);
|
||||
visit(t.getUpperBound(), null);
|
||||
seenVariables.remove(decl);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitWildcard(WildcardType t, Void p) {
|
||||
visit(t.getSuperBound());
|
||||
visit(t.getExtendsBound());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitExecutable(ExecutableType t, Void p) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitNoType(NoType t, Void p) {
|
||||
return null;//done
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitUnknown(TypeMirror t, Void p) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitUnion(UnionType t, Void p) {
|
||||
update("(");
|
||||
visit(t.getAlternatives());
|
||||
update(")");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitIntersection(IntersectionType t, Void p) {
|
||||
update("(");
|
||||
visit(t.getBounds());
|
||||
update(")");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(AnnotationValue av, Void p) {
|
||||
return av.accept(this, p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitBoolean(boolean b, Void p) {
|
||||
update(String.valueOf(b));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitByte(byte b, Void p) {
|
||||
update(String.valueOf(b));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitChar(char c, Void p) {
|
||||
update(String.valueOf(c));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitDouble(double d, Void p) {
|
||||
update(String.valueOf(d));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitFloat(float f, Void p) {
|
||||
update(String.valueOf(f));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitInt(int i, Void p) {
|
||||
update(String.valueOf(i));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitLong(long i, Void p) {
|
||||
update(String.valueOf(i));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitShort(short s, Void p) {
|
||||
update(String.valueOf(s));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitString(String s, Void p) {
|
||||
update(s);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitType(TypeMirror t, Void p) {
|
||||
return visit(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitEnumConstant(VariableElement c, Void p) {
|
||||
return visit(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitAnnotation(AnnotationMirror a, Void p) {
|
||||
updateAnnotation(a);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitArray(List<? extends AnnotationValue> vals, Void p) {
|
||||
update("(");
|
||||
for (AnnotationValue av : vals) {
|
||||
visit(av);
|
||||
}
|
||||
update(")");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitUnknown(AnnotationValue av, Void p) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitRequires(RequiresDirective d, Void p) {
|
||||
update("RequiresDirective");
|
||||
update(String.valueOf(d.isStatic()));
|
||||
update(String.valueOf(d.isTransitive()));
|
||||
update(d.getDependency().getQualifiedName());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitExports(ExportsDirective d, Void p) {
|
||||
update("ExportsDirective");
|
||||
update(d.getPackage().getQualifiedName());
|
||||
if (d.getTargetModules() != null) {
|
||||
for (ModuleElement me : d.getTargetModules()) {
|
||||
update(me.getQualifiedName());
|
||||
}
|
||||
} else {
|
||||
update("<none>");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitOpens(OpensDirective d, Void p) {
|
||||
update("OpensDirective");
|
||||
update(d.getPackage().getQualifiedName());
|
||||
if (d.getTargetModules() != null) {
|
||||
for (ModuleElement me : d.getTargetModules()) {
|
||||
update(me.getQualifiedName());
|
||||
}
|
||||
} else {
|
||||
update("<none>");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitUses(UsesDirective d, Void p) {
|
||||
update("UsesDirective");
|
||||
update(d.getService().getQualifiedName());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitProvides(ProvidesDirective d, Void p) {
|
||||
update("ProvidesDirective");
|
||||
update(d.getService().getQualifiedName());
|
||||
update("(");
|
||||
for (TypeElement impl : d.getImplementations()) {
|
||||
update(impl.getQualifiedName());
|
||||
}
|
||||
update(")");
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
281
make/jdk/src/classes/build/tools/depend/DependTest.java
Normal file
281
make/jdk/src/classes/build/tools/depend/DependTest.java
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 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 build.tools.depend;
|
||||
|
||||
import com.sun.source.util.Plugin;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
|
||||
public class DependTest {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
DependTest test = new DependTest();
|
||||
|
||||
test.setupClass();
|
||||
|
||||
test.testMethods();
|
||||
test.testFields();
|
||||
test.testModules();
|
||||
test.testAnnotations();
|
||||
}
|
||||
|
||||
public void testMethods() throws Exception {
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"public class Test {\n" +
|
||||
" public void test() {\n" +
|
||||
" }\n" +
|
||||
"}",
|
||||
true);
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"public class Test {\n" +
|
||||
" private void test() {\n" +
|
||||
" }\n" +
|
||||
"}",
|
||||
false);
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
" public void test() {\n" +
|
||||
" }\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
true);
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
" private void test() {\n" +
|
||||
" }\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
false);
|
||||
}
|
||||
|
||||
public void testFields() throws Exception {
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"public class Test {\n" +
|
||||
" public int test;\n" +
|
||||
"}",
|
||||
true);
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"public class Test {\n" +
|
||||
" private int test;\n" +
|
||||
"}",
|
||||
false);
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
" public static final int test = 0;\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"public class Test {\n" +
|
||||
" public static final int test = 1;\n" +
|
||||
"}",
|
||||
true);
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
" public int test;\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
true);
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
" private int test;\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
false);
|
||||
}
|
||||
|
||||
public void testAnnotations() throws Exception {
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"@SuppressWarnings(\"any\")\n" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
false);
|
||||
doOrdinaryTest("package test;" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
"package test;" +
|
||||
"@Deprecated\n" +
|
||||
"public class Test {\n" +
|
||||
"}",
|
||||
true);
|
||||
}
|
||||
|
||||
public void testModules() throws Exception {
|
||||
doModuleTest("module m { }",
|
||||
"module m { requires java.compiler; }",
|
||||
true);
|
||||
doModuleTest("module m { requires java.compiler; }",
|
||||
"module m { requires java.compiler; }",
|
||||
false);
|
||||
doModuleTest("module m { requires java.compiler; }",
|
||||
"module m { requires jdk.compiler; }",
|
||||
true);
|
||||
doModuleTest("module m { }",
|
||||
"module m { exports test; }",
|
||||
true);
|
||||
doModuleTest("module m { }",
|
||||
"module m { exports test to java.base; }",
|
||||
true);
|
||||
doModuleTest("module m { }",
|
||||
"module m { exports test to java.compiler; }",
|
||||
true);
|
||||
doModuleTest("module m { }",
|
||||
"module m { uses test.Test1; }",
|
||||
true);
|
||||
doModuleTest("module m { uses test.Test1; }",
|
||||
"module m { uses test.Test2; }",
|
||||
true);
|
||||
doModuleTest("module m { }",
|
||||
"module m { provides test.Test1 with test.TestImpl1; }",
|
||||
true);
|
||||
doModuleTest("module m { provides test.Test1 with test.TestImpl1; }",
|
||||
"module m { provides test.Test2 with test.TestImpl1; }",
|
||||
true);
|
||||
doModuleTest("module m { provides test.Test1 with test.TestImpl1; }",
|
||||
"module m { provides test.Test1 with test.TestImpl2; }",
|
||||
true);
|
||||
}
|
||||
|
||||
private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
private Path depend;
|
||||
private Path scratchServices;
|
||||
private Path scratchClasses;
|
||||
private Path apiHash;
|
||||
|
||||
private void setupClass() throws IOException {
|
||||
depend = Paths.get(Depend.class.getProtectionDomain().getCodeSource().getLocation().getPath());
|
||||
Path scratch = Files.createTempDirectory("depend-test");
|
||||
scratchServices = scratch.resolve("services");
|
||||
Path scratchClassesServices = scratchServices.resolve("META-INF").resolve("services");
|
||||
Files.createDirectories(scratchClassesServices);
|
||||
|
||||
try (OutputStream out = Files.newOutputStream(scratchClassesServices.resolve(Plugin.class.getName()))) {
|
||||
out.write(Depend.class.getName().getBytes());
|
||||
}
|
||||
|
||||
scratchClasses = scratch.resolve("classes");
|
||||
|
||||
Files.createDirectories(scratchClasses);
|
||||
|
||||
apiHash = scratch.resolve("api");
|
||||
}
|
||||
|
||||
private void doOrdinaryTest(String codeBefore, String codeAfter, boolean hashChangeExpected) throws Exception {
|
||||
List<String> options =
|
||||
Arrays.asList("-d", scratchClasses.toString(),
|
||||
"-processorpath", depend.toString() + File.pathSeparator + scratchServices.toString(),
|
||||
"-Xplugin:depend " + apiHash.toString());
|
||||
List<TestJavaFileObject> beforeFiles =
|
||||
Arrays.asList(new TestJavaFileObject("module-info", "module m { exports test; }"),
|
||||
new TestJavaFileObject("test.Test", codeBefore));
|
||||
compiler.getTask(null, null, null, options, null, beforeFiles).call();
|
||||
byte[] originalHash = Files.readAllBytes(apiHash);
|
||||
List<TestJavaFileObject> afterFiles =
|
||||
Arrays.asList(new TestJavaFileObject("module-info", "module m { exports test; }"),
|
||||
new TestJavaFileObject("test.Test", codeAfter));
|
||||
compiler.getTask(null, null, null, options, null, afterFiles).call();
|
||||
byte[] newHash = Files.readAllBytes(apiHash);
|
||||
|
||||
if (Arrays.equals(originalHash, newHash) ^ !hashChangeExpected) {
|
||||
throw new AssertionError("Unexpected hash state.");
|
||||
}
|
||||
}
|
||||
|
||||
private void doModuleTest(String codeBefore, String codeAfter, boolean hashChangeExpected) throws Exception {
|
||||
List<String> options =
|
||||
Arrays.asList("-d", scratchClasses.toString(),
|
||||
"-processorpath", depend.toString() + File.pathSeparator + scratchServices.toString(),
|
||||
"-Xplugin:depend " + apiHash.toString());
|
||||
List<TestJavaFileObject> beforeFiles =
|
||||
Arrays.asList(new TestJavaFileObject("module-info", codeBefore),
|
||||
new TestJavaFileObject("test.Test1", "package test; public interface Test1 {}"),
|
||||
new TestJavaFileObject("test.Test2", "package test; public interface Test2 {}"),
|
||||
new TestJavaFileObject("test.TestImpl1", "package test; public class TestImpl1 implements Test1, Test2 {}"),
|
||||
new TestJavaFileObject("test.TestImpl2", "package test; public class TestImpl2 implements Test1, Test2 {}"));
|
||||
compiler.getTask(null, null, null, options, null, beforeFiles).call();
|
||||
byte[] originalHash = Files.readAllBytes(apiHash);
|
||||
List<TestJavaFileObject> afterFiles =
|
||||
Arrays.asList(new TestJavaFileObject("module-info", codeAfter),
|
||||
new TestJavaFileObject("test.Test1", "package test; public interface Test1 {}"),
|
||||
new TestJavaFileObject("test.Test2", "package test; public interface Test2 {}"),
|
||||
new TestJavaFileObject("test.TestImpl1", "package test; public class TestImpl1 implements Test1, Test2 {}"),
|
||||
new TestJavaFileObject("test.TestImpl2", "package test; public class TestImpl2 implements Test1, Test2 {}"));
|
||||
compiler.getTask(null, null, null, options, null, afterFiles).call();
|
||||
byte[] newHash = Files.readAllBytes(apiHash);
|
||||
|
||||
if (Arrays.equals(originalHash, newHash) ^ !hashChangeExpected) {
|
||||
throw new AssertionError("Unexpected hash state.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestJavaFileObject extends SimpleJavaFileObject {
|
||||
|
||||
private final String code;
|
||||
|
||||
public TestJavaFileObject(String className, String code) throws URISyntaxException {
|
||||
super(new URI("mem:/" + className.replace('.', '/') + ".java"), Kind.SOURCE);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean arg0) throws IOException {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user