1 /* 2 * Copyright (c) 2021, 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 jdk.internal.jextract.impl; 27 28 import jdk.incubator.jextract.Declaration; 29 30 import java.io.IOException; 31 import java.io.UncheckedIOException; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.nio.file.StandardOpenOption; 35 import java.util.Comparator; 36 import java.util.EnumMap; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Set; 42 import java.util.TreeMap; 43 import java.util.TreeSet; 44 import java.util.stream.Collectors; 45 46 public class IncludeHelper { 47 48 public enum IncludeKind { 49 MACRO, 50 VAR, 51 FUNCTION, 52 TYPEDEF, 53 STRUCT, 54 UNION; 55 56 public String optionName() { 57 return "include-" + name().toLowerCase(); 58 } 59 60 static IncludeKind fromDeclaration(Declaration d) { 61 if (d instanceof Declaration.Constant) { 62 return MACRO; 63 } else if (d instanceof Declaration.Variable) { 64 return VAR; 65 } else if (d instanceof Declaration.Function) { 66 return FUNCTION; 67 } else if (d instanceof Declaration.Typedef) { 68 return TYPEDEF; 69 } else if (d instanceof Declaration.Scoped scoped) { 70 return fromScoped(scoped); 71 } else { 72 throw new IllegalStateException("Cannot get here!"); 73 } 74 } 75 76 static IncludeKind fromScoped(Declaration.Scoped scoped) { 77 return switch (scoped.kind()) { 78 case STRUCT -> IncludeKind.STRUCT; 79 case UNION -> IncludeKind.UNION; 80 default -> throw new IllegalStateException("Cannot get here!"); 81 }; 82 } 83 } 84 85 private final EnumMap<IncludeKind, Set<String>> includesSymbolNamesByKind = new EnumMap<>(IncludeKind.class); 86 private final Set<Declaration> usedDeclarations = new HashSet<>(); 87 public String dumpIncludesFile; 88 89 public void addSymbol(IncludeKind kind, String symbolName) { 90 Set<String> names = includesSymbolNamesByKind.computeIfAbsent(kind, (_unused) -> new HashSet<>()); 91 names.add(symbolName); 92 } 93 94 public boolean isIncluded(Declaration.Variable variable) { 95 return checkIncludedAndAddIfNeeded(IncludeKind.VAR, variable); 96 } 97 98 public boolean isIncluded(Declaration.Function function) { 99 return checkIncludedAndAddIfNeeded(IncludeKind.FUNCTION, function); 100 } 101 102 public boolean isIncluded(Declaration.Constant constant) { 103 return checkIncludedAndAddIfNeeded(IncludeKind.MACRO, constant); 104 } 105 106 public boolean isIncluded(Declaration.Typedef typedef) { 107 return checkIncludedAndAddIfNeeded(IncludeKind.TYPEDEF, typedef); 108 } 109 110 public boolean isIncluded(Declaration.Scoped scoped) { 111 return checkIncludedAndAddIfNeeded(IncludeKind.fromScoped(scoped), scoped); 112 } 113 114 private boolean checkIncludedAndAddIfNeeded(IncludeKind kind, Declaration declaration) { 115 boolean included = isIncludedInternal(kind, declaration); 116 if (included && dumpIncludesFile != null) { 117 usedDeclarations.add(declaration); 118 } 119 return included; 120 } 121 122 private boolean isIncludedInternal(IncludeKind kind, Declaration declaration) { 123 if (!isEnabled()) { 124 return true; 125 } else { 126 Set<String> names = includesSymbolNamesByKind.computeIfAbsent(kind, (_unused) -> new HashSet<>()); 127 return names.contains(declaration.name()); 128 } 129 } 130 131 public boolean isEnabled() { 132 return includesSymbolNamesByKind.size() > 0; 133 } 134 135 public void dumpIncludes() { 136 try (var writer = Files.newBufferedWriter(Path.of(dumpIncludesFile), StandardOpenOption.CREATE)) { 137 Map<Path, Set<Declaration>> declsByPath = usedDeclarations.stream() 138 .collect(Collectors.groupingBy(d -> d.pos().path(), 139 () -> new TreeMap<>(Path::compareTo), 140 Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Declaration::name))))); 141 String lineSep = ""; 142 for (Map.Entry<Path, Set<Declaration>> pathEntries : declsByPath.entrySet()) { 143 writer.append(lineSep); 144 writer.append("#### Extracted from: " + pathEntries.getKey().toString() + "\n\n"); 145 Map<IncludeKind, List<Declaration>> declsByKind = pathEntries.getValue().stream() 146 .collect(Collectors.groupingBy(IncludeKind::fromDeclaration)); 147 int maxLengthOptionCol = pathEntries.getValue().stream().mapToInt(d -> d.name().length()).max().getAsInt(); 148 maxLengthOptionCol += 2; // -- 149 maxLengthOptionCol += IncludeKind.FUNCTION.optionName().length(); // max option name 150 maxLengthOptionCol += 1; // space 151 for (Map.Entry<IncludeKind, List<Declaration>> kindEntries : declsByKind.entrySet()) { 152 for (Declaration d : kindEntries.getValue()) { 153 writer.append(String.format("%-" + maxLengthOptionCol + "s %s", 154 "--" + kindEntries.getKey().optionName() + " " + d.name(), 155 "# header: " + pathEntries.getKey() + "\n")); 156 } 157 } 158 lineSep = "\n"; 159 } 160 } catch (IOException exception) { 161 throw new UncheckedIOException(exception); 162 } 163 } 164 }