1 /*
2 * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
41 import java.nio.file.Paths;
42 import java.nio.file.attribute.BasicFileAttributes;
43 import java.util.stream.Stream;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.LinkedHashMap;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Map.Entry;
55 import java.util.Objects;
56 import java.util.Set;
57 import java.util.function.Function;
58 import java.util.function.Predicate;
59 import java.util.regex.Matcher;
60 import java.util.regex.Pattern;
61
62 import com.sun.tools.classfile.AccessFlags;
63 import com.sun.tools.classfile.Annotation;
64 import com.sun.tools.classfile.Annotation.Annotation_element_value;
65 import com.sun.tools.classfile.Annotation.Array_element_value;
66 import com.sun.tools.classfile.Annotation.Class_element_value;
67 import com.sun.tools.classfile.Annotation.Enum_element_value;
68 import com.sun.tools.classfile.Annotation.Primitive_element_value;
69 import com.sun.tools.classfile.Annotation.element_value;
70 import com.sun.tools.classfile.Annotation.element_value_pair;
71 import com.sun.tools.classfile.AnnotationDefault_attribute;
72 import com.sun.tools.classfile.Attribute;
73 import com.sun.tools.classfile.Attributes;
74 import com.sun.tools.classfile.ClassFile;
75 import com.sun.tools.classfile.ClassWriter;
76 import com.sun.tools.classfile.ConstantPool;
77 import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
78 import com.sun.tools.classfile.ConstantPool.CONSTANT_Double_info;
79 import com.sun.tools.classfile.ConstantPool.CONSTANT_Float_info;
80 import com.sun.tools.classfile.ConstantPool.CONSTANT_Integer_info;
103 import com.sun.tools.javac.util.Assert;
104 import com.sun.tools.javac.util.Pair;
105
106 /**
107 * A tool for processing the .sym.txt files. It allows to:
108 * * convert the .sym.txt into class/sig files for ct.sym
109 * * in cooperation with the adjacent history Probe, construct .sym.txt files for previous platforms
110 *
111 * To convert the .sym.txt files to class/sig files from ct.sym, run:
112 * java build.tool.symbolgenerator.CreateSymbols build-ctsym [JOINED_VERSIONS|SEPARATE] <platform-description-file> <target-directory>
113 *
114 * The <platform-description-file> is a file of this format:
115 * generate platforms <platform-ids-to-generate separate with ':'>
116 * platform version <platform-id1> files <.sym.txt files containing history data for given platform, separate with ':'>
117 * platform version <platform-id2> base <base-platform-id> files <.sym.txt files containing history data for given platform, separate with ':'>
118 *
119 * The content of platform "<base-platform-id>" is also automatically added to the content of
120 * platform "<platform-id2>", unless explicitly excluded in "<platform-id2>"'s .sym.txt files.
121 *
122 * To create the .sym.txt files, first run the history Probe for all the previous platforms:
123 * java build.tools.symbolgenerator.Probe <target-file-for-platform>
124 *
125 * Then create the .sym.txt files like this:
126 * java build.tools.symbolgenerator.CreateSymbols build-description <target-directory> <path-to-a-JDK-root> <include-list-file>
127 * <platform-id1> <target-file-for-platform1> "<none>"
128 * <platform-id2> <target-file-for-platform2> <diff-against-platform2>
129 * <platform-id3> <target-file-for-platform3> <diff-against-platform3>
130 * ...
131 *
132 * The <include-list-file> is a file that specifies classes that should be included/excluded.
133 * Lines that start with '+' represent class or package that should be included, '-' class or package
134 * that should be excluded. '/' should be used as package name delimiter, packages should end with '/'.
135 * Several include list files may be specified, separated by File.pathSeparator.
136 *
137 * When <diff-against-platformN> is specified, the .sym.txt files for platform N will only contain
138 * differences between platform N and the specified platform. The first platform (denoted F further)
139 * that is specified should use literal value "<none>", to have all the APIs of the platform written to
140 * the .sym.txt files. If there is an existing platform with full .sym.txt files in the repository,
141 * that platform should be used as the first platform to avoid unnecessary changes to the .sym.txt
142 * files. The <diff-against-platformN> for platform N should be determined as follows: if N < F, then
143 * <diff-against-platformN> should be N + 1. If F < N, then <diff-against-platformN> should be N - 1.
144 * If N is a custom/specialized sub-version of another platform N', then <diff-against-platformN> should be N'.
145 *
146 * To generate the .sym.txt files for OpenJDK 7 and 8:
147 * java build.tools.symbolgenerator.CreateSymbols build-description make/data/symbols $TOPDIR make/data/symbols/include.list
148 * 8 OpenJDK8.classes '<none>'
149 * 7 OpenJDK7.classes 8
150 *
151 * Note: the versions are expected to be a single character.
152 */
153 public class CreateSymbols {
154
155 //<editor-fold defaultstate="collapsed" desc="ct.sym construction">
156 /**Create sig files for ct.sym reading the classes description from the directory that contains
157 * {@code ctDescriptionFile}, using the file as a recipe to create the sigfiles.
158 */
159 @SuppressWarnings("unchecked")
160 public void createSymbols(String ctDescriptionFile, String ctSymLocation, CtSymKind ctSymKind) throws IOException {
161 ClassList classes = load(Paths.get(ctDescriptionFile));
162
163 splitHeaders(classes);
164
165 for (ClassDescription classDescription : classes) {
166 for (ClassHeaderDescription header : classDescription.header) {
896 Map<String, String> package2Modules = buildPackage2Modules(jdkRoot);
897 Map<String, List<ClassDescription>> module2Classes = new HashMap<>();
898
899 for (ClassDescription clazz : classes) {
900 String pack;
901 int lastSlash = clazz.name.lastIndexOf('/');
902 if (lastSlash != (-1)) {
903 pack = clazz.name.substring(0, lastSlash).replace('/', '.');
904 } else {
905 pack = "";
906 }
907 String module = package2Modules.get(pack);
908
909 if (module == null) {
910 module = "java.base";
911
912 OUTER: while (!pack.isEmpty()) {
913 for (Entry<String, String> p2M : package2Modules.entrySet()) {
914 if (p2M.getKey().startsWith(pack)) {
915 module = p2M.getValue();
916 break;
917 }
918 }
919 int dot = pack.lastIndexOf('.');
920 if (dot == (-1))
921 break;
922 pack = pack.substring(0, dot);
923 }
924 }
925 module2Classes.computeIfAbsent(module, m -> new ArrayList<>())
926 .add(clazz);
927 }
928
929 for (Entry<String, List<ClassDescription>> e : module2Classes.entrySet()) {
930 for (VersionDescription desc : versions) {
931 Path f = descDest.resolve(e.getKey() + "-" + desc.version + ".sym.txt");
932 Files.createDirectories(f.getParent());
933 try (Writer out = Files.newBufferedWriter(f)) {
934 for (ClassDescription clazz : e.getValue()) {
935 clazz.write(out, desc.primaryBaseline, desc.version);
936 }
937 }
938 }
939 }
940 }
941
942 //<editor-fold defaultstate="collapsed" desc="Class Reading">
943 //non-final for tests:
944 public static String PROFILE_ANNOTATION = "Ljdk/Profile+Annotation;";
945 public static boolean ALLOW_NON_EXISTING_CLASSES = false;
946
947 private void inspectClassFile(InputStream in, ClassList classes, ExcludeIncludeList excludesIncludes, String version) throws IOException, ConstantPoolException {
948 ClassFile cf = ClassFile.read(in);
949
950 if (!excludesIncludes.accepts(cf.getName())) {
951 return ;
952 }
953
954 ClassHeaderDescription headerDesc = new ClassHeaderDescription();
955
956 headerDesc.flags = cf.access_flags.flags;
957
|
1 /*
2 * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
41 import java.nio.file.Paths;
42 import java.nio.file.attribute.BasicFileAttributes;
43 import java.util.stream.Stream;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.LinkedHashMap;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Map.Entry;
55 import java.util.Objects;
56 import java.util.Set;
57 import java.util.function.Function;
58 import java.util.function.Predicate;
59 import java.util.regex.Matcher;
60 import java.util.regex.Pattern;
61 import java.util.stream.Collectors;
62
63 import com.sun.tools.classfile.AccessFlags;
64 import com.sun.tools.classfile.Annotation;
65 import com.sun.tools.classfile.Annotation.Annotation_element_value;
66 import com.sun.tools.classfile.Annotation.Array_element_value;
67 import com.sun.tools.classfile.Annotation.Class_element_value;
68 import com.sun.tools.classfile.Annotation.Enum_element_value;
69 import com.sun.tools.classfile.Annotation.Primitive_element_value;
70 import com.sun.tools.classfile.Annotation.element_value;
71 import com.sun.tools.classfile.Annotation.element_value_pair;
72 import com.sun.tools.classfile.AnnotationDefault_attribute;
73 import com.sun.tools.classfile.Attribute;
74 import com.sun.tools.classfile.Attributes;
75 import com.sun.tools.classfile.ClassFile;
76 import com.sun.tools.classfile.ClassWriter;
77 import com.sun.tools.classfile.ConstantPool;
78 import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
79 import com.sun.tools.classfile.ConstantPool.CONSTANT_Double_info;
80 import com.sun.tools.classfile.ConstantPool.CONSTANT_Float_info;
81 import com.sun.tools.classfile.ConstantPool.CONSTANT_Integer_info;
104 import com.sun.tools.javac.util.Assert;
105 import com.sun.tools.javac.util.Pair;
106
107 /**
108 * A tool for processing the .sym.txt files. It allows to:
109 * * convert the .sym.txt into class/sig files for ct.sym
110 * * in cooperation with the adjacent history Probe, construct .sym.txt files for previous platforms
111 *
112 * To convert the .sym.txt files to class/sig files from ct.sym, run:
113 * java build.tool.symbolgenerator.CreateSymbols build-ctsym [JOINED_VERSIONS|SEPARATE] <platform-description-file> <target-directory>
114 *
115 * The <platform-description-file> is a file of this format:
116 * generate platforms <platform-ids-to-generate separate with ':'>
117 * platform version <platform-id1> files <.sym.txt files containing history data for given platform, separate with ':'>
118 * platform version <platform-id2> base <base-platform-id> files <.sym.txt files containing history data for given platform, separate with ':'>
119 *
120 * The content of platform "<base-platform-id>" is also automatically added to the content of
121 * platform "<platform-id2>", unless explicitly excluded in "<platform-id2>"'s .sym.txt files.
122 *
123 * To create the .sym.txt files, first run the history Probe for all the previous platforms:
124 * <jdk-N>/bin/java build.tools.symbolgenerator.Probe <classes-for-N>
125 *
126 * Where <classes-for-N> is a name of a file into which the classes from the bootclasspath of <jdk-N>
127 * will be written.
128 *
129 * Then create the .sym.txt files like this:
130 * java build.tools.symbolgenerator.CreateSymbols build-description <target-directory> <path-to-a-JDK-root> <include-list-file>
131 * <platform-id1> <target-file-for-platform1> "<none>"
132 * <platform-id2> <target-file-for-platform2> <diff-against-platform2>
133 * <platform-id3> <target-file-for-platform3> <diff-against-platform3>
134 * ...
135 *
136 * The <include-list-file> is a file that specifies classes that should be included/excluded.
137 * Lines that start with '+' represent class or package that should be included, '-' class or package
138 * that should be excluded. '/' should be used as package name delimiter, packages should end with '/'.
139 * Several include list files may be specified, separated by File.pathSeparator.
140 *
141 * When <diff-against-platformN> is specified, the .sym.txt files for platform N will only contain
142 * differences between platform N and the specified platform. The first platform (denoted F further)
143 * that is specified should use literal value "<none>", to have all the APIs of the platform written to
144 * the .sym.txt files. If there is an existing platform with full .sym.txt files in the repository,
145 * that platform should be used as the first platform to avoid unnecessary changes to the .sym.txt
146 * files. The <diff-against-platformN> for platform N should be determined as follows: if N < F, then
147 * <diff-against-platformN> should be N + 1. If F < N, then <diff-against-platformN> should be N - 1.
148 * If N is a custom/specialized sub-version of another platform N', then <diff-against-platformN> should be N'.
149 *
150 * To generate the .sym.txt files for OpenJDK 7 and 8:
151 * <jdk-7>/bin/java build.tools.symbolgenerator.Probe OpenJDK7.classes
152 * <jdk-8>/bin/java build.tools.symbolgenerator.Probe OpenJDK8.classes
153 * java build.tools.symbolgenerator.CreateSymbols build-description make/data/symbols $TOPDIR make/data/symbols/include.list
154 * 8 OpenJDK8.classes '<none>'
155 * 7 OpenJDK7.classes 8
156 *
157 * Note: the versions are expected to be a single character.
158 */
159 public class CreateSymbols {
160
161 //<editor-fold defaultstate="collapsed" desc="ct.sym construction">
162 /**Create sig files for ct.sym reading the classes description from the directory that contains
163 * {@code ctDescriptionFile}, using the file as a recipe to create the sigfiles.
164 */
165 @SuppressWarnings("unchecked")
166 public void createSymbols(String ctDescriptionFile, String ctSymLocation, CtSymKind ctSymKind) throws IOException {
167 ClassList classes = load(Paths.get(ctDescriptionFile));
168
169 splitHeaders(classes);
170
171 for (ClassDescription classDescription : classes) {
172 for (ClassHeaderDescription header : classDescription.header) {
902 Map<String, String> package2Modules = buildPackage2Modules(jdkRoot);
903 Map<String, List<ClassDescription>> module2Classes = new HashMap<>();
904
905 for (ClassDescription clazz : classes) {
906 String pack;
907 int lastSlash = clazz.name.lastIndexOf('/');
908 if (lastSlash != (-1)) {
909 pack = clazz.name.substring(0, lastSlash).replace('/', '.');
910 } else {
911 pack = "";
912 }
913 String module = package2Modules.get(pack);
914
915 if (module == null) {
916 module = "java.base";
917
918 OUTER: while (!pack.isEmpty()) {
919 for (Entry<String, String> p2M : package2Modules.entrySet()) {
920 if (p2M.getKey().startsWith(pack)) {
921 module = p2M.getValue();
922 break OUTER;
923 }
924 }
925 int dot = pack.lastIndexOf('.');
926 if (dot == (-1))
927 break;
928 pack = pack.substring(0, dot);
929 }
930 }
931 module2Classes.computeIfAbsent(module, m -> new ArrayList<>())
932 .add(clazz);
933 }
934
935 Path symbolsFile = descDest.resolve("symbols");
936
937 Files.createDirectories(symbolsFile.getParent());
938
939 try (Writer symbolsOut = Files.newBufferedWriter(symbolsFile)) {
940 Map<VersionDescription, List<Path>> outputFiles = new LinkedHashMap<>();
941
942 for (Entry<String, List<ClassDescription>> e : module2Classes.entrySet()) {
943 for (VersionDescription desc : versions) {
944 Path f = descDest.resolve(e.getKey() + "-" + desc.version + ".sym.txt");
945 try (Writer out = Files.newBufferedWriter(f)) {
946 for (ClassDescription clazz : e.getValue()) {
947 clazz.write(out, desc.primaryBaseline, desc.version);
948 }
949 }
950 outputFiles.computeIfAbsent(desc, d -> new ArrayList<>())
951 .add(f);
952 }
953 }
954 symbolsOut.append("generate platforms ")
955 .append(versions.stream()
956 .map(v -> v.version)
957 .collect(Collectors.joining(":")))
958 .append("\n");
959 for (Entry<VersionDescription, List<Path>> versionFileEntry : outputFiles.entrySet()) {
960 symbolsOut.append("platform version ")
961 .append(versionFileEntry.getKey().version);
962 if (versionFileEntry.getKey().primaryBaseline != null) {
963 symbolsOut.append(" base ")
964 .append(versionFileEntry.getKey().primaryBaseline);
965 }
966 symbolsOut.append(" files ")
967 .append(versionFileEntry.getValue()
968 .stream()
969 .map(p -> p.getFileName().toString())
970 .sorted()
971 .collect(Collectors.joining(":")))
972 .append("\n");
973 }
974 }
975 }
976
977 //<editor-fold defaultstate="collapsed" desc="Class Reading">
978 //non-final for tests:
979 public static String PROFILE_ANNOTATION = "Ljdk/Profile+Annotation;";
980 public static boolean ALLOW_NON_EXISTING_CLASSES = false;
981
982 private void inspectClassFile(InputStream in, ClassList classes, ExcludeIncludeList excludesIncludes, String version) throws IOException, ConstantPoolException {
983 ClassFile cf = ClassFile.read(in);
984
985 if (!excludesIncludes.accepts(cf.getName())) {
986 return ;
987 }
988
989 ClassHeaderDescription headerDesc = new ClassHeaderDescription();
990
991 headerDesc.flags = cf.access_flags.flags;
992
|