001/* 002 * Copyright 2017-2022 original authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.micronaut.maven; 017 018import org.apache.commons.io.FilenameUtils; 019import org.apache.maven.execution.MavenSession; 020import org.apache.maven.project.MavenProject; 021import org.apache.maven.toolchain.Toolchain; 022import org.apache.maven.toolchain.ToolchainManager; 023import org.codehaus.plexus.util.Os; 024 025import java.io.File; 026import java.io.IOException; 027import java.nio.file.Files; 028import java.nio.file.Path; 029import java.nio.file.Paths; 030import java.util.ArrayList; 031import java.util.List; 032import java.util.stream.Collectors; 033import java.util.stream.Stream; 034 035import static io.micronaut.maven.AbstractDockerMojo.MOSTLY_STATIC_NATIVE_IMAGE_GRAALVM_FLAG; 036import static io.micronaut.maven.RunMojo.THIS_PLUGIN; 037 038/** 039 * Utility methods for different mojos. 040 */ 041public final class MojoUtils { 042 043 private static final String JAVA = "java"; 044 045 private MojoUtils() { 046 } 047 048 public static String findJavaExecutable(ToolchainManager toolchainManager, MavenSession mavenSession) { 049 String executable; 050 Toolchain toolchain = toolchainManager.getToolchainFromBuildContext("jdk", mavenSession); 051 if (toolchain != null) { 052 executable = toolchain.findTool(JAVA); 053 } else { 054 var javaBinariesDir = new File(new File(System.getProperty("java.home")), "bin"); 055 if (Os.isFamily(Os.FAMILY_UNIX)) { 056 executable = new File(javaBinariesDir, JAVA).getAbsolutePath(); 057 } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { 058 executable = new File(javaBinariesDir, "java.exe").getAbsolutePath(); 059 } else { 060 executable = JAVA; 061 } 062 } 063 return executable; 064 } 065 066 public static List<String> computeNativeImageArgs(List<String> nativeImageBuildArgs, String baseImageRun, String argsFile) { 067 var allNativeImageBuildArgs = new ArrayList<String>(); 068 if (nativeImageBuildArgs != null && !nativeImageBuildArgs.isEmpty()) { 069 allNativeImageBuildArgs.addAll(nativeImageBuildArgs); 070 } 071 if (baseImageRun.contains("distroless") && !allNativeImageBuildArgs.contains(MOSTLY_STATIC_NATIVE_IMAGE_GRAALVM_FLAG)) { 072 allNativeImageBuildArgs.add(MOSTLY_STATIC_NATIVE_IMAGE_GRAALVM_FLAG); 073 } 074 075 List<String> argsFileContent = parseNativeImageArgsFile(argsFile).toList(); 076 allNativeImageBuildArgs.addAll(argsFileContent); 077 return allNativeImageBuildArgs; 078 } 079 080 private static Stream<String> parseNativeImageArgsFile(String argsFile) { 081 Path argsFilePath = Paths.get(argsFile); 082 if (Files.exists(argsFilePath)) { 083 List<String> args; 084 try { 085 args = Files.readAllLines(argsFilePath); 086 } catch (IOException e) { 087 throw new RuntimeException("Could not read the args file: " + argsFilePath, e); 088 } 089 if (args.contains("-cp")) { 090 int cpPosition = args.indexOf("-cp"); 091 args.remove(cpPosition); 092 args.remove(cpPosition); 093 } 094 095 return args.stream() 096 .filter(arg -> !arg.startsWith("-H:Name")) 097 .filter(arg -> !arg.startsWith("-H:Class")) 098 .filter(arg -> !arg.startsWith("-H:Path")) 099 .flatMap(arg -> { 100 if (arg.startsWith("@")) { 101 String fileName = arg.substring(1); 102 return parseNativeImageArgsFile(fileName); 103 } else if (arg.startsWith("\\Q") && arg.endsWith("\\E")) { 104 // start the search at length - 3 to skip \Q or \E at the end 105 int lastIndexOfSlash = arg.lastIndexOf(File.separator, arg.length() - 3); 106 return Stream.of("\\Q/home/app/libs/" + arg.substring(lastIndexOfSlash + 1)); 107 } else if (arg.startsWith("-H:ConfigurationFileDirectories")) { 108 return Stream.of(parseConfigurationFilesDirectoriesArg(arg)); 109 } else { 110 return Stream.of(arg); 111 } 112 }); 113 } else { 114 throw new RuntimeException("Unable to find args file: " + argsFilePath); 115 } 116 } 117 118 static String parseConfigurationFilesDirectoriesArg(String arg) { 119 String[] split = arg.split("="); 120 String[] directories = split[1].split(","); 121 String separator = "/"; 122 if (arg.contains("generateResourceConfig") || arg.contains("generateTestResourceConfig")) { 123 return Stream.of(directories) 124 .map(FilenameUtils::separatorsToUnix) 125 .map(directory -> { 126 String[] splitDirectory = directory.split(separator); 127 return "/home/app/" + splitDirectory[splitDirectory.length - 1]; 128 }) 129 .collect(Collectors.joining(",")) 130 .transform(s -> "-H:ConfigurationFileDirectories=" + s); 131 } else { 132 return Stream.of(directories) 133 .map(FilenameUtils::separatorsToUnix) 134 .map(directory -> { 135 String[] splitDirectory = directory.split(separator); 136 String last4Directories = splitDirectory[splitDirectory.length - 4] + separator + 137 splitDirectory[splitDirectory.length - 3] + separator + 138 splitDirectory[splitDirectory.length - 2] + separator + 139 splitDirectory[splitDirectory.length - 1]; 140 return "/home/app/graalvm-reachability-metadata/" + last4Directories; 141 }) 142 .collect(Collectors.joining(",")) 143 .transform(s -> "-H:ConfigurationFileDirectories=" + s); 144 } 145 } 146 147 /** 148 * Checks if the project has the Micronaut Maven plugin defined. 149 * 150 * @param project the Maven project 151 * @return true if the project has the Micronaut Maven plugin defined 152 */ 153 public static boolean hasMicronautMavenPlugin(MavenProject project) { 154 String[] parts = THIS_PLUGIN.split(":"); 155 String groupId = parts[0]; 156 String artifactId = parts[1]; 157 return project.getBuildPlugins().stream() 158 .anyMatch(p -> p.getGroupId().equals(groupId) && p.getArtifactId().equals(artifactId)); 159 } 160}