63 import jdk.internal.util.jar.JarIndex;
64
65 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
66 import static java.util.jar.JarFile.MANIFEST_NAME;
67 import static java.util.stream.Collectors.joining;
68 import static java.util.stream.Collectors.toSet;
69 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
70
71 /**
72 * This class implements a simple utility for creating files in the JAR
73 * (Java Archive) file format. The JAR format is based on the ZIP file
74 * format, with optional meta-information stored in a MANIFEST entry.
75 */
76 public
77 class Main {
78 String program;
79 PrintWriter out, err;
80 String fname, mname, ename;
81 String zname = "";
82 String rootjar = null;
83
84 private static final int BASE_VERSION = 0;
85
86 class Entry {
87 final String basename;
88 final String entryname;
89 final File file;
90 final boolean isDir;
91
92 Entry(int version, File file) {
93 this.file = file;
94 String path = file.getPath();
95 if (file.isDirectory()) {
96 isDir = true;
97 path = path.endsWith(File.separator) ? path :
98 path + File.separator;
99 } else {
100 isDir = false;
101 }
102 EntryName en = new EntryName(path, version);
819 }
820 }
821 return true;
822 }
823
824 private static Set<String> findPackages(ZipFile zf) {
825 return zf.stream()
826 .filter(e -> e.getName().endsWith(".class"))
827 .map(e -> toPackageName(e))
828 .filter(pkg -> pkg.length() > 0)
829 .distinct()
830 .collect(Collectors.toSet());
831 }
832
833 private static String toPackageName(ZipEntry entry) {
834 return toPackageName(entry.getName());
835 }
836
837 private static String toPackageName(String path) {
838 assert path.endsWith(".class");
839 int index = path.lastIndexOf('/');
840 if (index != -1) {
841 return path.substring(0, index).replace('/', '.');
842 } else {
843 return "";
844 }
845 }
846
847 /**
848 * Expands list of files to process into full list of all files that
849 * can be found by recursively descending directories.
850 */
851 void expand(File dir,
852 String[] files,
853 boolean isUpdate,
854 Map<String,Path> moduleInfoPaths,
855 int version)
856 throws IOException
857 {
858 if (files == null)
859 return;
860
861 for (int i = 0; i < files.length; i++) {
862 File f;
863 if (dir == null)
864 f = new File(files[i]);
865 else
866 f = new File(dir, files[i]);
867
868 Entry entry = new Entry(version, f);
869 String entryName = entry.entryname;
870
871 if (f.isFile()) {
872 if (entryName.endsWith(MODULE_INFO)) {
873 moduleInfoPaths.put(entryName, f.toPath());
874 if (isUpdate)
875 entryMap.put(entryName, entry);
876 } else if (entries.add(entry)) {
877 jarEntries.add(entryName);
878 if (entry.basename.endsWith(".class") && !entryName.startsWith(VERSIONS_DIR))
879 packages.add(toPackageName(entry.basename));
880 if (isUpdate)
881 entryMap.put(entryName, entry);
882 }
883 } else if (f.isDirectory()) {
884 if (entries.add(entry)) {
885 if (isUpdate) {
886 entryMap.put(entryName, entry);
887 }
888 expand(f, f.list(), isUpdate, moduleInfoPaths, version);
889 }
890 } else {
891 error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
892 ok = false;
893 }
894 }
895 }
896
897 /**
898 * Creates a new JAR file.
1051 // do our own compression
1052 ZipEntry e2 = new ZipEntry(name);
1053 e2.setMethod(e.getMethod());
1054 e2.setTime(e.getTime());
1055 e2.setComment(e.getComment());
1056 e2.setExtra(e.getExtra());
1057 if (e.getMethod() == ZipEntry.STORED) {
1058 e2.setSize(e.getSize());
1059 e2.setCrc(e.getCrc());
1060 }
1061 zos.putNextEntry(e2);
1062 copy(zis, zos);
1063 } else { // replace with the new files
1064 Entry ent = entryMap.get(name);
1065 addFile(zos, ent);
1066 entryMap.remove(name);
1067 entries.remove(ent);
1068 }
1069
1070 jarEntries.add(name);
1071 if (name.endsWith(".class") && !(name.startsWith(VERSIONS_DIR)))
1072 packages.add(toPackageName(name));
1073 }
1074 }
1075
1076 // add the remaining new files
1077 for (Entry entry : entries) {
1078 addFile(zos, entry);
1079 }
1080 if (!foundManifest) {
1081 if (newManifest != null) {
1082 Manifest m = new Manifest(newManifest);
1083 updateOk = !isAmbiguousMainClass(m);
1084 if (updateOk) {
1085 if (!updateManifest(m, zos)) {
1086 updateOk = false;
1087 }
1088 }
1089 } else if (ename != null) {
1090 if (!updateManifest(new Manifest(), zos)) {
1091 updateOk = false;
1745 */
1746 void fatalError(String s) {
1747 error(program + ": " + s);
1748 }
1749
1750 /**
1751 * Print an output message; like verbose output and the like
1752 */
1753 protected void output(String s) {
1754 out.println(s);
1755 }
1756
1757 /**
1758 * Print an error message; like something is broken
1759 */
1760 void error(String s) {
1761 err.println(s);
1762 }
1763
1764 /**
1765 * Main routine to start program.
1766 */
1767 public static void main(String args[]) {
1768 Main jartool = new Main(System.out, System.err, "jar");
1769 System.exit(jartool.run(args) ? 0 : 1);
1770 }
1771
1772 /**
1773 * An OutputStream that doesn't send its output anywhere, (but could).
1774 * It's here to find the CRC32 of an input file, necessary for STORED
1775 * mode in ZIP.
1776 */
1777 private static class CRC32OutputStream extends java.io.OutputStream {
1778 final CRC32 crc = new CRC32();
1779 long n = 0;
1780
1781 CRC32OutputStream() {}
1782
1783 public void write(int r) throws IOException {
1784 crc.update(r);
1958 .collect(Collectors.toSet());
1959 if (missing.size() > 0) {
1960 missing.stream().forEach(s -> fatalError(formatMsg("error.missing.provider", s)));
1961 return false;
1962 }
1963 return true;
1964 }
1965
1966 /**
1967 * Adds extended modules attributes to the given module-info's. The given
1968 * Map values are updated in-place. Returns false if an error occurs.
1969 */
1970 private boolean addExtendedModuleAttributes(Map<String,byte[]> moduleInfos)
1971 throws IOException
1972 {
1973 assert !moduleInfos.isEmpty() && moduleInfos.get(MODULE_INFO) != null;
1974
1975 ByteBuffer bb = ByteBuffer.wrap(moduleInfos.get(MODULE_INFO));
1976 ModuleDescriptor rd = ModuleDescriptor.read(bb);
1977
1978 Set<String> exports = rd.exports()
1979 .stream()
1980 .map(Exports::source)
1981 .collect(toSet());
1982
1983 Set<String> conceals = packages.stream()
1984 .filter(p -> !exports.contains(p))
1985 .collect(toSet());
1986
1987 for (Map.Entry<String,byte[]> e: moduleInfos.entrySet()) {
1988 ModuleDescriptor vd = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue()));
1989 if (!(isValidVersionedDescriptor(vd, rd)))
1990 return false;
1991 e.setValue(extendedInfoBytes(rd, vd, e.getValue(), conceals));
1992 }
1993 return true;
1994 }
1995
1996 private static boolean isPlatformModule(String name) {
1997 return name.startsWith("java.") || name.startsWith("jdk.");
1998 }
1999
2000 /**
2001 * Tells whether or not the given versioned module descriptor's attributes
2002 * are valid when compared against the given root module descriptor.
2003 *
2004 * A versioned module descriptor must be identical to the root module
2005 * descriptor, with two exceptions:
2006 * - A versioned descriptor can have different non-public `requires`
2007 * clauses of platform ( `java.*` and `jdk.*` ) modules, and
2008 * - A versioned descriptor can have different `uses` clauses, even of
2009 * service types defined outside of the platform modules.
2010 */
2011 private boolean isValidVersionedDescriptor(ModuleDescriptor vd,
2012 ModuleDescriptor rd)
2013 throws IOException
2014 {
2015 if (!rd.name().equals(vd.name())) {
|
63 import jdk.internal.util.jar.JarIndex;
64
65 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
66 import static java.util.jar.JarFile.MANIFEST_NAME;
67 import static java.util.stream.Collectors.joining;
68 import static java.util.stream.Collectors.toSet;
69 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
70
71 /**
72 * This class implements a simple utility for creating files in the JAR
73 * (Java Archive) file format. The JAR format is based on the ZIP file
74 * format, with optional meta-information stored in a MANIFEST entry.
75 */
76 public
77 class Main {
78 String program;
79 PrintWriter out, err;
80 String fname, mname, ename;
81 String zname = "";
82 String rootjar = null;
83 Set<String> concealedPackages = new HashSet<>();
84
85 private static final int BASE_VERSION = 0;
86
87 class Entry {
88 final String basename;
89 final String entryname;
90 final File file;
91 final boolean isDir;
92
93 Entry(int version, File file) {
94 this.file = file;
95 String path = file.getPath();
96 if (file.isDirectory()) {
97 isDir = true;
98 path = path.endsWith(File.separator) ? path :
99 path + File.separator;
100 } else {
101 isDir = false;
102 }
103 EntryName en = new EntryName(path, version);
820 }
821 }
822 return true;
823 }
824
825 private static Set<String> findPackages(ZipFile zf) {
826 return zf.stream()
827 .filter(e -> e.getName().endsWith(".class"))
828 .map(e -> toPackageName(e))
829 .filter(pkg -> pkg.length() > 0)
830 .distinct()
831 .collect(Collectors.toSet());
832 }
833
834 private static String toPackageName(ZipEntry entry) {
835 return toPackageName(entry.getName());
836 }
837
838 private static String toPackageName(String path) {
839 assert path.endsWith(".class");
840 int index;
841 if (path.startsWith(VERSIONS_DIR)) {
842 index = path.indexOf('/', VERSIONS_DIR.length());
843 if (index <= 0) {
844 return "";
845 }
846 path = path.substring(index + 1);
847 }
848 index = path.lastIndexOf('/');
849 if (index != -1) {
850 return path.substring(0, index).replace('/', '.');
851 } else {
852 return "";
853 }
854 }
855
856 /**
857 * Expands list of files to process into full list of all files that
858 * can be found by recursively descending directories.
859 */
860 void expand(File dir,
861 String[] files,
862 boolean isUpdate,
863 Map<String,Path> moduleInfoPaths,
864 int version)
865 throws IOException
866 {
867 if (files == null)
868 return;
869
870 for (int i = 0; i < files.length; i++) {
871 File f;
872 if (dir == null)
873 f = new File(files[i]);
874 else
875 f = new File(dir, files[i]);
876
877 Entry entry = new Entry(version, f);
878 String entryName = entry.entryname;
879
880 if (f.isFile()) {
881 if (entryName.endsWith(MODULE_INFO)) {
882 moduleInfoPaths.put(entryName, f.toPath());
883 if (isUpdate)
884 entryMap.put(entryName, entry);
885 } else if (entries.add(entry)) {
886 jarEntries.add(entryName);
887 if (entry.basename.endsWith(".class"))
888 packages.add(toPackageName(entry.basename));
889 if (isUpdate)
890 entryMap.put(entryName, entry);
891 }
892 } else if (f.isDirectory()) {
893 if (entries.add(entry)) {
894 if (isUpdate) {
895 entryMap.put(entryName, entry);
896 }
897 expand(f, f.list(), isUpdate, moduleInfoPaths, version);
898 }
899 } else {
900 error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
901 ok = false;
902 }
903 }
904 }
905
906 /**
907 * Creates a new JAR file.
1060 // do our own compression
1061 ZipEntry e2 = new ZipEntry(name);
1062 e2.setMethod(e.getMethod());
1063 e2.setTime(e.getTime());
1064 e2.setComment(e.getComment());
1065 e2.setExtra(e.getExtra());
1066 if (e.getMethod() == ZipEntry.STORED) {
1067 e2.setSize(e.getSize());
1068 e2.setCrc(e.getCrc());
1069 }
1070 zos.putNextEntry(e2);
1071 copy(zis, zos);
1072 } else { // replace with the new files
1073 Entry ent = entryMap.get(name);
1074 addFile(zos, ent);
1075 entryMap.remove(name);
1076 entries.remove(ent);
1077 }
1078
1079 jarEntries.add(name);
1080 if (name.endsWith(".class"))
1081 packages.add(toPackageName(name));
1082 }
1083 }
1084
1085 // add the remaining new files
1086 for (Entry entry : entries) {
1087 addFile(zos, entry);
1088 }
1089 if (!foundManifest) {
1090 if (newManifest != null) {
1091 Manifest m = new Manifest(newManifest);
1092 updateOk = !isAmbiguousMainClass(m);
1093 if (updateOk) {
1094 if (!updateManifest(m, zos)) {
1095 updateOk = false;
1096 }
1097 }
1098 } else if (ename != null) {
1099 if (!updateManifest(new Manifest(), zos)) {
1100 updateOk = false;
1754 */
1755 void fatalError(String s) {
1756 error(program + ": " + s);
1757 }
1758
1759 /**
1760 * Print an output message; like verbose output and the like
1761 */
1762 protected void output(String s) {
1763 out.println(s);
1764 }
1765
1766 /**
1767 * Print an error message; like something is broken
1768 */
1769 void error(String s) {
1770 err.println(s);
1771 }
1772
1773 /**
1774 * Print a warning message
1775 */
1776 void warn(String s) {
1777 err.println(s);
1778 }
1779
1780 /**
1781 * Main routine to start program.
1782 */
1783 public static void main(String args[]) {
1784 Main jartool = new Main(System.out, System.err, "jar");
1785 System.exit(jartool.run(args) ? 0 : 1);
1786 }
1787
1788 /**
1789 * An OutputStream that doesn't send its output anywhere, (but could).
1790 * It's here to find the CRC32 of an input file, necessary for STORED
1791 * mode in ZIP.
1792 */
1793 private static class CRC32OutputStream extends java.io.OutputStream {
1794 final CRC32 crc = new CRC32();
1795 long n = 0;
1796
1797 CRC32OutputStream() {}
1798
1799 public void write(int r) throws IOException {
1800 crc.update(r);
1974 .collect(Collectors.toSet());
1975 if (missing.size() > 0) {
1976 missing.stream().forEach(s -> fatalError(formatMsg("error.missing.provider", s)));
1977 return false;
1978 }
1979 return true;
1980 }
1981
1982 /**
1983 * Adds extended modules attributes to the given module-info's. The given
1984 * Map values are updated in-place. Returns false if an error occurs.
1985 */
1986 private boolean addExtendedModuleAttributes(Map<String,byte[]> moduleInfos)
1987 throws IOException
1988 {
1989 assert !moduleInfos.isEmpty() && moduleInfos.get(MODULE_INFO) != null;
1990
1991 ByteBuffer bb = ByteBuffer.wrap(moduleInfos.get(MODULE_INFO));
1992 ModuleDescriptor rd = ModuleDescriptor.read(bb);
1993
1994 concealedPackages = findConcealedPackages(rd);
1995
1996 for (Map.Entry<String,byte[]> e: moduleInfos.entrySet()) {
1997 ModuleDescriptor vd = ModuleDescriptor.read(ByteBuffer.wrap(e.getValue()));
1998 if (!(isValidVersionedDescriptor(vd, rd)))
1999 return false;
2000 e.setValue(extendedInfoBytes(rd, vd, e.getValue(), concealedPackages));
2001 }
2002 return true;
2003 }
2004
2005 private Set<String> findConcealedPackages(ModuleDescriptor md){
2006 Objects.requireNonNull(md);
2007
2008 Set<String> exports = md.exports()
2009 .stream()
2010 .map(Exports::source)
2011 .collect(toSet());
2012
2013 return packages.stream()
2014 .filter(p -> !p.equals(""))
2015 .filter(p -> !exports.contains(p))
2016 .collect(toSet());
2017 }
2018
2019 private static boolean isPlatformModule(String name) {
2020 return name.startsWith("java.") || name.startsWith("jdk.");
2021 }
2022
2023 /**
2024 * Tells whether or not the given versioned module descriptor's attributes
2025 * are valid when compared against the given root module descriptor.
2026 *
2027 * A versioned module descriptor must be identical to the root module
2028 * descriptor, with two exceptions:
2029 * - A versioned descriptor can have different non-public `requires`
2030 * clauses of platform ( `java.*` and `jdk.*` ) modules, and
2031 * - A versioned descriptor can have different `uses` clauses, even of
2032 * service types defined outside of the platform modules.
2033 */
2034 private boolean isValidVersionedDescriptor(ModuleDescriptor vd,
2035 ModuleDescriptor rd)
2036 throws IOException
2037 {
2038 if (!rd.name().equals(vd.name())) {
|