1 /*
   2  * Copyright (c) 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
  23  * questions.
  24  */
  25 
  26 package build.tools.module;
  27 
  28 import java.io.File;
  29 import java.io.IOException;
  30 import java.io.UncheckedIOException;
  31 import java.nio.file.Files;
  32 import java.nio.file.NoSuchFileException;
  33 import java.nio.file.Path;
  34 import java.nio.file.Paths;
  35 import java.nio.file.attribute.BasicFileAttributes;
  36 import java.util.HashSet;
  37 import java.util.Set;
  38 import java.util.stream.Collectors;
  39 
  40 /**
  41  * GenJdepsModulesXml augments the input modules.xml file(s)
  42  * to include the module membership from the given path to
  43  * the JDK exploded image.  The output file is used by jdeps
  44  * to analyze dependencies and enforce module boundaries.
  45  *
  46  * The input modules.xml file defines the modular structure of
  47  * the JDK as described in JEP 200: The Modular JDK
  48  * (http://openjdk.java.net/jeps/200).
  49  *
  50  * $ java build.tools.module.GenJdepsModulesXml \
  51  *        -o com/sun/tools/jdeps/resources/modules.xml \
  52  *        -mp $OUTPUTDIR/modules \
  53  *        top/modules.xml
  54  */
  55 public final class GenJdepsModulesXml {
  56     private final static String USAGE =
  57         "Usage: GenJdepsModulesXml -o <output file> -mp build/modules path-to-modules-xml";
  58 
  59     public static void main(String[] args) throws Exception {
  60         Path outfile = null;
  61         Path modulepath = null;
  62         int i = 0;
  63         while (i < args.length) {
  64             String arg = args[i];
  65             if (arg.equals("-o")) {
  66                 outfile = Paths.get(args[i+1]);
  67                 i = i+2;
  68             } else if (arg.equals("-mp")) {
  69                 modulepath = Paths.get(args[i+1]);
  70                 i = i+2;
  71                 if (!Files.isDirectory(modulepath)) {
  72                     System.err.println(modulepath + " is not a directory");
  73                     System.exit(1);
  74                 }
  75             } else {
  76                 break;
  77             }
  78         }
  79         if (outfile == null || modulepath == null || i >= args.length) {
  80             System.err.println(USAGE);
  81             System.exit(-1);
  82         }
  83 
  84         GenJdepsModulesXml gentool = new GenJdepsModulesXml(modulepath);
  85         Set<Module> modules = new HashSet<>();
  86         for (; i < args.length; i++) {
  87             Path p = Paths.get(args[i]);
  88             modules.addAll(ModulesXmlReader.readModules(p)
  89                     .stream()
  90                     .map(gentool::buildIncludes)
  91                     .collect(Collectors.toSet()));
  92         }
  93 
  94         Files.createDirectories(outfile.getParent());
  95         ModulesXmlWriter.writeModules(modules, outfile);
  96     }
  97 
  98     final Path modulepath;
  99     public GenJdepsModulesXml(Path modulepath) {
 100         this.modulepath = modulepath;
 101     }
 102 
 103     private static String packageName(Path p) {
 104         return packageName(p.toString().replace(File.separatorChar, '/'));
 105     }
 106     private static String packageName(String name) {
 107         int i = name.lastIndexOf('/');
 108         return (i > 0) ? name.substring(0, i).replace('/', '.') : "";
 109     }
 110 
 111     private static boolean includes(String name) {
 112         return name.endsWith(".class");
 113     }
 114 
 115     public Module buildIncludes(Module module) {
 116         Module.Builder mb = new Module.Builder(module);
 117         Path mclasses = modulepath.resolve(module.name());
 118         try {
 119             Files.find(mclasses, Integer.MAX_VALUE, (Path p, BasicFileAttributes attr)
 120                          -> includes(p.getFileName().toString()))
 121                  .map(p -> packageName(mclasses.relativize(p)))
 122                  .forEach(mb::include);
 123         } catch (NoSuchFileException e) {
 124             // aggregate module may not have class
 125         } catch (IOException ioe) {
 126             throw new UncheckedIOException(ioe);
 127         }
 128         return mb.build();
 129     }
 130 }