kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/extractors/openjdk11/java_wrapper/java_wrapper.go (about) 1 /* 2 * Copyright 2019 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Binary java_wrapper wraps the real java command to invoke the standalone java extractor 18 // in parallel with the genuine compilation command. 19 // As it interjects itself between make and a real java command, all options are 20 // provided via environment variables. See the corresponding consts and java_extractor for details. 21 package main 22 23 import ( 24 "fmt" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "regexp" 29 "strings" 30 31 "kythe.io/kythe/go/util/log" 32 33 "bitbucket.org/creachadair/shell" 34 "bitbucket.org/creachadair/stringset" 35 "golang.org/x/sys/unix" 36 ) 37 38 const ( 39 javaCommandVar = "KYTHE_JAVA_COMMAND" // Path to the real java command, required. 40 extractorJarVar = "KYTHE_JAVA_EXTRACTOR_JAR" // Path to the javac_extractor jar, required. 41 kytheOutputVar = "KYTHE_OUTPUT_DIRECTORY" // Extraction output directory, required. 42 kytheTargetVar = "KYTHE_ANALYSIS_TARGET" // Extraction analysis target to set, defaults to the java module name. 43 excludeModulesVar = "KYTHE_OPENJDK11_EXCLUDE_MODULES" // Names of for which to skip extraction. 44 ) 45 46 var ( 47 modulePattern = regexp.MustCompile("^@/.*/_the.(.*)_batch.tmp$") 48 ) 49 50 func moduleName() string { 51 path := os.Args[len(os.Args)-1] 52 // Hackish way to determine the likely module being compiled. 53 repl := modulePattern.ReplaceAllString(path, "$1") 54 if repl == path { // No match, use dirname and basename to form the module. 55 return filepath.Base(filepath.Dir(path)) + "#" + filepath.Base(path) 56 } 57 return repl 58 } 59 60 func outputDir() string { 61 if val := os.Getenv(kytheOutputVar); val != "" { 62 return val 63 } 64 log.Fatal("ERROR: KYTHE_OUTPUT_DIRECTORY not set") 65 return "" 66 } 67 68 func mustGetEnvPath(key string) string { 69 if val := os.Getenv(key); val != "" { 70 if _, err := os.Stat(val); err != nil { 71 log.Fatalf("invalid %s: %v", key, err) 72 } 73 return val 74 } 75 log.Fatal(key + " not set") 76 return "" 77 } 78 79 func loadExclusions() stringset.Set { 80 if value := os.Getenv(excludeModulesVar); value != "" { 81 return stringset.FromKeys(strings.Split(value, ",")) 82 } 83 return stringset.Set{} 84 } 85 86 func javaCommand() string { 87 return mustGetEnvPath(javaCommandVar) 88 } 89 90 func extractorJar() string { 91 return mustGetEnvPath(extractorJarVar) 92 } 93 94 func extractorArgs(args []string, jar string) []string { 95 isJavac := false 96 var result []string 97 for len(args) > 0 { 98 var a string 99 var v string 100 switch a, args = shift(args); a { 101 case "-m", "--module": 102 v, args = shift(args) 103 if !strings.HasSuffix(v, ".javac.Main") { 104 isJavac = false 105 break 106 } 107 isJavac = true 108 result = append(result, 109 "--add-modules=java.logging,java.sql", 110 "--add-exports=jdk.compiler.interim/com.sun.tools.javac.main=ALL-UNNAMED", 111 "--add-exports=jdk.compiler.interim/com.sun.tools.javac.util=ALL-UNNAMED", 112 "--add-exports=jdk.compiler.interim/com.sun.tools.javac.file=ALL-UNNAMED", 113 "--add-exports=jdk.compiler.interim/com.sun.tools.javac.api=ALL-UNNAMED", 114 "--add-exports=jdk.compiler.interim/com.sun.tools.javac.code=ALL-UNNAMED", 115 "-jar", jar, "-Xprefer:source") 116 case "--doclint-format": 117 _, args = shift(args) 118 case "-Werror": 119 default: 120 switch { 121 case strings.HasPrefix(a, "-Xplugin:depend"), strings.HasPrefix(a, "-Xlint:"), strings.HasPrefix(a, "-Xdoclint"): 122 case strings.HasPrefix(a, "-Xmx"): 123 result = append(result, "-Xmx3G") 124 case !isJavac && strings.HasPrefix(a, "--") && len(args) > 0 && !strings.HasPrefix(args[0], "-"): 125 // Add an = separator between "--arg" and its value for JVM arguments 126 // in order to be friendlier to Bazel Java "launchers". 127 // The JVM generally accepts either form, but the generic argument processing 128 // in many launchers requires the "=" separator. 129 v, args = shift(args) 130 result = append(result, a+"="+v) 131 default: 132 result = append(result, a) 133 } 134 } 135 } 136 // As we can only do anything meaningful with Java compilations, 137 // but wrap the java binary, don't attempt to extract other invocations. 138 if !isJavac { 139 return nil 140 } 141 return result 142 } 143 144 func extractorEnv() []string { 145 env := os.Environ() 146 env = setEnvDefault(env, kytheTargetVar, moduleName()) 147 return env 148 } 149 150 func setEnvDefault(env []string, key, def string) []string { 151 if val := os.Getenv(key); val == "" { 152 env = append(env, key+"="+def) 153 } 154 return env 155 } 156 157 func shift(args []string) (string, []string) { 158 if len(args) > 0 { 159 return args[0], args[1:] 160 } 161 return "", nil 162 } 163 164 func main() { 165 excludeModules := loadExclusions() 166 java := javaCommand() 167 jar := extractorJar() 168 if args := extractorArgs(os.Args[1:], jar); len(args) > 0 { 169 if excludeModules.Contains(moduleName()) { 170 log.Infof("*** Skipping: %s", moduleName()) 171 } else { 172 cmd := exec.Command(java, args...) 173 cmd.Env = extractorEnv() 174 log.Infof("*** Extracting: %s", moduleName()) 175 if output, err := cmd.CombinedOutput(); err != nil { 176 w, err := os.Create(filepath.Join(outputDir(), moduleName()+".err")) 177 if err != nil { 178 log.Fatalf("Error creating error log for module %s: %v", moduleName(), err) 179 } 180 fmt.Fprintf(w, "--- %s\n", shell.Join(args)) 181 w.Write(output) 182 w.Close() 183 184 // Log, but don't abort, on extraction failures. 185 log.Errorf("extractor failure for module %s: %v", moduleName(), err) 186 } 187 } 188 } 189 // Always end by running the java command directly, as "java". 190 os.Args[0] = "java" 191 log.Fatal(unix.Exec(java, os.Args, os.Environ())) // If exec returns at all, it's an error. 192 }