001/* 002 * JDrupes Builder 003 * Copyright (C) 2025 Michael N. Lipp 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <https://www.gnu.org/licenses/>. 017 */ 018 019package org.jdrupes.builder.startup; 020 021import java.net.MalformedURLException; 022import java.net.URL; 023import java.net.URLClassLoader; 024import java.util.Arrays; 025import java.util.Collections; 026import java.util.logging.Logger; 027import java.util.stream.Stream; 028import org.jdrupes.builder.api.BuildException; 029import org.jdrupes.builder.api.FileResource; 030import org.jdrupes.builder.api.FileTree; 031import static org.jdrupes.builder.api.Intend.*; 032import org.jdrupes.builder.api.Launcher; 033import org.jdrupes.builder.api.Project; 034import org.jdrupes.builder.api.Resource; 035import org.jdrupes.builder.api.ResourceRequest; 036import org.jdrupes.builder.api.ResourceType; 037import org.jdrupes.builder.api.RootProject; 038import org.jdrupes.builder.core.LauncherSupport; 039import org.jdrupes.builder.java.ClasspathElement; 040import org.jdrupes.builder.java.CompilationResources; 041import org.jdrupes.builder.mvnrepo.MvnRepoLookup; 042 043/// A default implementation of a [Launcher]. The launcher first builds 044/// the user's JDrupes Builder project, using the JDrupes Builder project 045/// defined by [BootstrapRoot] and [BootstrapBuild]. The default action 046/// of [BootstrapRoot] adds the results from the bootstrap build 047/// to the classpath and launches the actual JDrupes Builder project. 048/// 049public class BootstrapLauncher extends AbstractLauncher { 050 051 /// The log. 052 protected final Logger log = Logger.getLogger(getClass().getName()); 053 private RootProject rootProject; 054 055 /// Instantiates a new bootstrap launcher. An instance of the class 056 /// passed as argument is created and used as root project for the 057 /// build. 058 /// 059 /// Unless the root project is the only project, the root project 060 /// must declare dependencies, else the subprojects won't be 061 /// instantiated. 062 /// 063 /// @param rootPrjCls the root project 064 /// @param args the args 065 /// 066 @SuppressWarnings("PMD.UseVarargs") 067 public BootstrapLauncher( 068 Class<? extends RootProject> rootPrjCls, String[] args) { 069 super(args); 070 unwrapBuildException(() -> { 071 rootProject = LauncherSupport.createProjects( 072 rootPrjCls, Collections.emptyList(), jdbldProps, commandLine); 073 074 // Add build extensions to the build project. 075 var mvnLookup = new MvnRepoLookup(); 076 Arrays.asList(jdbldProps 077 .getProperty(BootstrapBuild.BUILD_EXTENSIONS, "").split(",")) 078 .stream().map(String::trim).filter(c -> !c.isBlank()) 079 .forEach(mvnLookup::resolve); 080 ((Project) rootProject.project(BootstrapBuild.class)) 081 .dependency(Expose, mvnLookup); 082 @SuppressWarnings("PMD.UseDiamondOperator") 083 var cpUrls = rootProject.get( 084 new ResourceRequest<ClasspathElement>( 085 new ResourceType<CompilationResources>() {})) 086 .map(cpe -> { 087 try { 088 if (cpe instanceof FileTree tree) { 089 return tree.root().toFile().toURI().toURL(); 090 } 091 return ((FileResource) cpe).path().toFile().toURI() 092 .toURL(); 093 } catch (MalformedURLException e) { 094 // Cannot happen 095 throw new BuildException(e); 096 } 097 }).toArray(URL[]::new); 098 new DirectLauncher(new URLClassLoader(cpUrls, 099 Thread.currentThread().getContextClassLoader()), args); 100 return null; 101 }); 102 } 103 104 @Override 105 public <T extends Resource> Stream<T> provide(ResourceRequest<T> request) { 106 return unwrapBuildException(() -> { 107 // Provide requested resource, handling all exceptions here 108 var result = rootProject.get(request).toList(); 109 return result.stream(); 110 }); 111 } 112 113 /// The main method. 114 /// 115 /// @param args the arguments 116 /// 117 public static void main(String[] args) { 118 new BootstrapLauncher(BootstrapRoot.class, args); 119 } 120}