001/* 002 * Copyright (C) 2022 - 2024, the original author or 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 * http://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.github.ascopes.jct.compilers; 017 018import org.apiguardian.api.API; 019import org.apiguardian.api.API.Status; 020 021/** 022 * Function representing a configuration operation that can be applied to a compiler. 023 * 024 * <p>This can allow encapsulating common configuration logic across tests into a single place. 025 * 026 * <p>Implementations of this interface should declare the exception type that the implementation 027 * can throw when invoked. If this is not a checked exception, or no case exists where an exception 028 * could be thrown, then this can be set to {@link RuntimeException}. 029 * 030 * <p>The following demonstrates an example usage of this interface. The implementation configures 031 * a specific annotation processor and sets an annotation processor flag. 032 * 033 * <pre><code> 034 * class MyAnnotationProcessorConfigurer implements JctCompilerConfigurer<RuntimeException> { 035 * {@literal @Override} 036 * public void configure(JctCompiler compiler) { 037 * compiler 038 * .addAnnotationProcessors(new MyAnnotationProcessor()) 039 * .addAnnotationProcessorOptions("MyAnnotationProcessor.debug=true"); 040 * } 041 * } 042 * </code></pre> 043 * 044 * <p>...tests can then make use of this configurer directly: 045 * 046 * <pre><code> 047 * {@literal @Test} 048 * void theCompilationSucceedsAsExpected() { 049 * try (var workspace = Workspaces.newWorkspace()) { 050 * // Given 051 * ... 052 * 053 * var compiler = JctCompilers 054 * .newPlatformCompiler() 055 * .release(17) 056 * .configure(new MyAnnotationProcessorConfigurer()); 057 * 058 * // When 059 * var compilation = compiler.compile(workspace); 060 * 061 * // Then 062 * ... 063 * } 064 * } 065 * </code></pre> 066 * 067 * <p>Since this is a functional interface, configurers can be lambda expressions, anonymous 068 * objects, or method references. 069 * 070 * <pre><code> 071 * compiler 072 * .configure(c -> c.release(11)) 073 * .configure(this::configureFailures); 074 * </code></pre> 075 * 076 * <p>The JUnit support allows for specifying these configurers in an annotation instead. This 077 * will apply the configurer before passing it to the test as a parameter: 078 * 079 * <pre><code> 080 * {@literal @JavacCompilersTest(configurers = {MyAnnotationProcessorConfigurer.class})} 081 * void theCompilationSucceedsAsExpected(JctCompiler compiler) { 082 * // ... 083 * } 084 * </code></pre> 085 * 086 * <p>Note that in this case, the configurer must be an outer class or a static nested class 087 * rather than a Lambda expression, anonymous class, class instance, or nested class. It must also 088 * have a single public no-arguments constructor in order to be accessible. 089 * 090 * @param <E> the exception that may be thrown by the configurer. 091 * @author Ashley Scopes 092 * @since 0.0.1 093 */ 094@API(since = "0.0.1", status = Status.STABLE) 095@FunctionalInterface 096public interface JctCompilerConfigurer<E extends Exception> { 097 098 /** 099 * Apply configuration logic to the given compiler. 100 * 101 * @param compiler the compiler. 102 * @throws E any exception that may be thrown by the configurer. 103 */ 104 void configure(JctCompiler compiler) throws E; 105}