import org.eclipse.jdt.annotation.Nullable;
/**
- * Type of generated file. Four most common kinds are captured in {@link #RESOURCE}, {@link #SOURCE},
- * {@link #TEST_RESOURCE} and {@link #TEST_SOURCE}, but others may be externally defined.
+ * Type of generated file. Two most common kinds are captured in {@link #RESOURCE}, {@link #SOURCE}, but others may be
+ * externally defined.
*
* <p>
* Users of {@link FileGenerator} are expected to provide sensible mapping of {@link GeneratedFileType} to their
*/
public static final GeneratedFileType SOURCE = new GeneratedFileType("source");
- /**
- * A generated test resource file. This file should be part of test resources.
- */
- public static final GeneratedFileType TEST_RESOURCE = new GeneratedFileType("test-resource");
-
- /**
- * A generated test source file. This file should be part of test sources.
- */
- public static final GeneratedFileType TEST_SOURCE = new GeneratedFileType("test-source");
-
private static final ImmutableMap<String, GeneratedFileType> WELL_KNOWN = ImmutableMap.of(
- RESOURCE.name(), RESOURCE, TEST_RESOURCE.name(), TEST_RESOURCE,
- SOURCE.name(), SOURCE, TEST_SOURCE.name(), TEST_SOURCE);
+ RESOURCE.name(), RESOURCE, SOURCE.name(), SOURCE);
private final String name;
Verifier vrf = setUp("test-parent/UnknownGenerator/", true);
vrf.verifyTextInLog("[INFO] yang-to-sources: Code generator instantiated from "
+ "org.opendaylight.yangtools.yang2sources.spi.CodeGeneratorTestImpl");
- vrf.verifyTextInLog("Failed to instantiate code generator unknown");
+ vrf.verifyTextInLog("on project unknown-generator: Failed to find code generator class unknown");
+ vrf.verifyTextInLog("MojoFailureException: Failed to find code generator class unknown");
vrf.verifyTextInLog("java.lang.ClassNotFoundException: unknown");
}
v2.assertFileNotPresent(buildDir + "/classes/META-INF/yang/types3@2013-02-27.yang");
}
+ @Test
+ public void testFileGenerator() throws Exception {
+ Verifier v1 = setUp("test-parent/FileGenerator/", false);
+ v1.executeGoal("clean");
+ v1.executeGoal("package");
+
+ String buildDir = getMavenBuildDirectory(v1);
+
+ v1.assertFilePresent(buildDir + "/generated-sources/"
+ + "org.opendaylight.yangtools.yang2sources.spi.TestFileGenerator/fooGenSource.test");
+ v1.assertFilePresent(buildDir + "/generated-resources/"
+ + "org.opendaylight.yangtools.yang2sources.spi.TestFileGenerator/foo-gen-resource");
+ v1.assertFilePresent(buildDir + "/../src/main/java/fooSource.test");
+ v1.assertFilePresent(buildDir + "/../src/main/resources/foo-resource");
+ }
+
private static String getMavenBuildDirectory(final Verifier verifier) throws IOException {
final Properties sp = new Properties();
final Path path = new File(verifier.getBasedir() + "/it-project.properties").toPath();
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>test-parent</artifactId>
+ <version>@project.version@</version>
+ </parent>
+
+ <artifactId>file-generator</artifactId>
+
+ <properties>
+ <target.dir>${project.build.directory}</target.dir>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <version>${project.version}</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>../files</yangFilesRootDir>
+ <inspectDependencies>false</inspectDependencies>
+ <fileGenerators>
+ <fileGenerator>
+ <identifier>org.opendaylight.yangtools.yang2sources.spi.TestFileGenerator</identifier>
+ <configuration>
+ <prefix>foo</prefix>
+ </configuration>
+ </fileGenerator>
+ </fileGenerators>
+ </configuration>
+ </execution>
+ </executions>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin-spi</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>properties-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
</configuration>
</execution>
</executions>
- <dependencies>
- <dependency>
- <groupId>org.opendaylight.yangtools</groupId>
- <artifactId>yang-maven-plugin-spi</artifactId>
- <version>${project.version}</version>
- <type>test-jar</type>
- </dependency>
- </dependencies>
</plugin>
</plugins>
</build>
<modules>
<module>additional-config</module>
<module>correct</module>
+ <module>file-generator</module>
<module>generate-test1</module>
<module>generate-test2</module>
<module>generator</module>
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.spi;
+
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
+import java.util.Set;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFile;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileLifecycle;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFilePath;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileType;
+import org.opendaylight.yangtools.plugin.generator.api.ModuleResourceResolver;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+@NonNullByDefault
+final class TestFileGenerator implements FileGenerator {
+ private final String prefix;
+
+ TestFileGenerator(final String prefix) {
+ this.prefix = prefix;
+ }
+
+ @Override
+ public Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generateFiles(final EffectiveModelContext context,
+ final Set<Module> localModules, final ModuleResourceResolver moduleResourcePathResolver) {
+ if (prefix == null) {
+ return ImmutableTable.of();
+ }
+
+ return ImmutableTable.<GeneratedFileType, GeneratedFilePath, GeneratedFile>builder()
+ .put(GeneratedFileType.SOURCE, GeneratedFilePath.ofFilePath(prefix + "Source.test"),
+ GeneratedFile.of(GeneratedFileLifecycle.PERSISTENT, "source"))
+ .put(GeneratedFileType.RESOURCE, GeneratedFilePath.ofFilePath(prefix + "-resource"),
+ GeneratedFile.of(GeneratedFileLifecycle.PERSISTENT, "test-resource"))
+ .put(GeneratedFileType.SOURCE, GeneratedFilePath.ofFilePath(prefix + "GenSource.test"),
+ GeneratedFile.of(GeneratedFileLifecycle.TRANSIENT, "source"))
+ .put(GeneratedFileType.RESOURCE, GeneratedFilePath.ofFilePath(prefix + "-gen-resource"),
+ GeneratedFile.of(GeneratedFileLifecycle.TRANSIENT, "resource"))
+ .build();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.spi;
+
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.plugin.generator.api.AbstractFileGeneratorFactory;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
+
+@NonNullByDefault
+public final class TestFileGeneratorFactory extends AbstractFileGeneratorFactory {
+ public static final String PREFIX = "prefix";
+
+ public TestFileGeneratorFactory() {
+ super(TestFileGenerator.class.getName());
+ }
+
+ @Override
+ public FileGenerator newFileGenerator(final Map<String, String> configuration) {
+ return new TestFileGenerator(configuration.get(PREFIX));
+ }
+}
--- /dev/null
+org.opendaylight.yangtools.yang2sources.spi.TestFileGeneratorFactory
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Stopwatch;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
+import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonatype.plexus.build.incremental.BuildContext;
+
+@NonNullByDefault
+final class CodeGeneratorTask extends GeneratorTask<CodeGeneratorTaskFactory> {
+ private static final Logger LOG = LoggerFactory.getLogger(CodeGeneratorTask.class);
+
+ private final File outputDir;
+
+ CodeGeneratorTask(final CodeGeneratorTaskFactory factory, final ContextHolder context, final File outputDir) {
+ super(factory, context);
+ this.outputDir = requireNonNull(outputDir);
+ }
+
+ @Override
+ Collection<File> execute(final CodeGeneratorTaskFactory factory, final ContextHolder modelContext,
+ final BuildContext buildContext) throws IOException {
+ final Stopwatch watch = Stopwatch.createStarted();
+ final BasicCodeGenerator gen = factory.generator();
+ final boolean mark;
+ if (gen instanceof BuildContextAware) {
+ ((BuildContextAware)gen).setBuildContext(buildContext);
+ mark = false;
+ } else {
+ mark = true;
+ }
+
+ LOG.debug("Sources will be generated to {}", outputDir);
+ Collection<File> sources = gen.generateSources(modelContext.getContext(), outputDir,
+ modelContext.getYangModules(), modelContext);
+ if (sources == null) {
+ sources = List.of();
+ }
+
+ LOG.debug("Sources generated by {}: {}", gen.getClass(), sources);
+ LOG.info("Sources generated by {}: {} in {}", gen.getClass(), sources.size(), watch);
+
+ if (mark) {
+ sources.forEach(buildContext::refresh);
+ }
+
+ return sources;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
+import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator.ImportResolutionMode;
+import org.opendaylight.yangtools.yang2sources.spi.MavenProjectAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Bridge to legacy {@link BasicCodeGenerator} generation.
+ *
+ * @author Robert Varga
+ */
+final class CodeGeneratorTaskFactory extends GeneratorTaskFactory {
+ private static final Logger LOG = LoggerFactory.getLogger(CodeGeneratorTaskFactory.class);
+
+ private final @NonNull BasicCodeGenerator gen;
+ private final CodeGeneratorArg cfg;
+
+ private CodeGeneratorTaskFactory(final ImportResolutionMode importMode, final BasicCodeGenerator gen,
+ final CodeGeneratorArg cfg) {
+ super(importMode.toFileGeneratorMode());
+ this.gen = requireNonNull(gen);
+ this.cfg = requireNonNull(cfg);
+ }
+
+ static GeneratorTaskFactory create(final CodeGeneratorArg cfg) throws MojoFailureException {
+ cfg.check();
+
+ final String codegenClass = cfg.getCodeGeneratorClass();
+ final Class<?> clazz;
+ try {
+ clazz = Class.forName(codegenClass);
+ } catch (ClassNotFoundException e) {
+ throw new MojoFailureException("Failed to find code generator class " + codegenClass, e);
+ }
+ final Class<? extends BasicCodeGenerator> typedClass;
+ try {
+ typedClass = clazz.asSubclass(BasicCodeGenerator.class);
+ } catch (ClassCastException e) {
+ throw new MojoFailureException("Code generator " + clazz + " does not implement "
+ + BasicCodeGenerator.class, e);
+ }
+ final Constructor<? extends BasicCodeGenerator> ctor;
+ try {
+ ctor = typedClass.getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new MojoFailureException("Code generator " + clazz
+ + " does not have an accessible no-argument constructor", e);
+ }
+ final @NonNull BasicCodeGenerator gen;
+ try {
+ gen = ctor.newInstance();
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ throw new MojoFailureException("Failed to instantiate code generator " + clazz, e);
+ }
+ LOG.debug("Code generator instantiated from {}", codegenClass);
+
+ ImportResolutionMode importMode = gen.getImportResolutionMode();
+ if (importMode == null) {
+ importMode = ImportResolutionMode.REVISION_EXACT_OR_LATEST;
+ }
+ return new CodeGeneratorTaskFactory(importMode, gen, cfg);
+ }
+
+ @Override
+ CodeGeneratorTask createTask(final MavenProject project, final ContextHolder context) {
+ if (gen instanceof MavenProjectAware) {
+ ((MavenProjectAware)gen).setMavenProject(project);
+ }
+
+ LOG.debug("Project root dir is {}", project.getBasedir());
+ LOG.debug("{} Additional configuration picked up for : {}: {}", YangToSourcesProcessor.LOG_PREFIX,
+ generatorName(), cfg.getAdditionalConfiguration());
+
+ final File outputDir = cfg.getOutputBaseDir(project);
+ project.addCompileSourceRoot(outputDir.getAbsolutePath());
+
+ gen.setAdditionalConfig(cfg.getAdditionalConfiguration());
+ File resourceBaseDir = cfg.getResourceBaseDir(project);
+
+ final Resource res = new Resource();
+ res.setDirectory(resourceBaseDir.getPath());
+ project.addResource(res);
+
+ gen.setResourceBaseDir(resourceBaseDir);
+ LOG.debug("Folder: {} marked as resources for generator: {}", resourceBaseDir, generatorName());
+ return new CodeGeneratorTask(this, context, outputDir);
+ }
+
+ @Override
+ BasicCodeGenerator generator() {
+ return gen;
+ }
+
+ @Override
+ ToStringHelper addToStringProperties(final ToStringHelper helper) {
+ return super.addToStringProperties(helper).add("configuration", cfg);
+ }
+}
import static java.util.Objects.requireNonNull;
+import com.google.common.base.MoreObjects;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
}
public File getOutputBaseDir(final MavenProject project) {
- if (outputBaseDir == null) {
- return null;
- }
return outputBaseDir.isAbsolute() ? outputBaseDir : new File(project.getBasedir(), outputBaseDir.getPath());
}
- public abstract void check();
+ public void check() {
+ requireNonNull(outputBaseDir,
+ "outputBaseDir is null. Please provide a valid outputBaseDir value in the pom.xml");
+ }
/**
* Configuration argument for code generator class and output directory.
@Override
public void check() {
+ super.check();
requireNonNull(codeGeneratorClass, "codeGeneratorClass for CodeGenerator cannot be null");
}
public Map<String, String> getAdditionalConfiguration() {
return additionalConfiguration;
}
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).omitNullValues()
+ .add("resourceDir", resourceBaseDir)
+ .add("configuration", additionalConfiguration)
+ .toString();
+ }
}
}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.plugin;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Identifiable;
+
+public final class FileGeneratorArg implements Identifiable<String> {
+ @Parameter
+ private final Map<String, String> configuration = new HashMap<>();
+
+ @Parameter(required = true)
+ private String identifier;
+
+ public FileGeneratorArg() {
+
+ }
+
+ public FileGeneratorArg(final String identifier) {
+ this.identifier = requireNonNull(identifier);
+ }
+
+ public FileGeneratorArg(final String identifier, final Map<String, String> configuration) {
+ this(identifier);
+ this.configuration.putAll(configuration);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return verifyNotNull(identifier);
+ }
+
+ public @NonNull Map<String, String> getConfiguration() {
+ return Collections.unmodifiableMap(configuration);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this).add("id", identifier).add("configuration", configuration).toString();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.plugin;
+
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.Table;
+import com.google.common.collect.Table.Cell;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.maven.model.Build;
+import org.apache.maven.model.Resource;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFile;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFilePath;
+import org.opendaylight.yangtools.plugin.generator.api.GeneratedFileType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonatype.plexus.build.incremental.BuildContext;
+
+final class FileGeneratorTask extends GeneratorTask<FileGeneratorTaskFactory> {
+ private static final Logger LOG = LoggerFactory.getLogger(FileGeneratorTask.class);
+
+ private final Map<GeneratedFileType, File> persistentDirs = new HashMap<>(4);
+ private final Map<GeneratedFileType, File> transientDirs = new HashMap<>(4);
+ private final MavenProject project;
+ private final File buildDir;
+ private final String suffix;
+
+ FileGeneratorTask(final @NonNull FileGeneratorTaskFactory factory, final @NonNull ContextHolder context,
+ final MavenProject project) {
+ super(factory, context);
+
+ final Build build = project.getBuild();
+ final String buildDirectory = build.getDirectory();
+ this.buildDir = new File(buildDirectory);
+ this.suffix = factory.getIdentifier();
+ this.project = project;
+ }
+
+ @Override
+ Collection<File> execute(final FileGeneratorTaskFactory factory, final ContextHolder modelContext,
+ final BuildContext buildContext) throws FileGeneratorException, IOException {
+ // Step one: determine what files are going to be generated
+ final Stopwatch sw = Stopwatch.createStarted();
+ final FileGenerator gen = factory.generator();
+ final Table<GeneratedFileType, GeneratedFilePath, GeneratedFile> generatedFiles = gen.generateFiles(
+ modelContext.getContext(), modelContext.getYangModules(), modelContext);
+ LOG.info("{}: Defined {} files in {}", suffix, generatedFiles.size(), sw);
+
+ // Step two: create generation tasks for each target file and group them by parent directory
+ sw.reset().start();
+ final ListMultimap<File, WriteTask> dirs = MultimapBuilder.hashKeys().arrayListValues().build();
+ for (Cell<GeneratedFileType, GeneratedFilePath, GeneratedFile> cell : generatedFiles.cellSet()) {
+ final GeneratedFile file = cell.getValue();
+ final String relativePath = cell.getColumnKey().getPath();
+ final File target;
+ switch (file.getLifecycle()) {
+ case PERSISTENT:
+ target = new File(persistentPath(cell.getRowKey()), relativePath);
+ if (target.exists()) {
+ LOG.debug("Skipping existing persistent {}", target);
+ continue;
+ }
+ break;
+ case TRANSIENT:
+ target = new File(transientPath(cell.getRowKey()), relativePath);
+ break;
+ default:
+ throw new IllegalStateException("Unsupported file type in " + file);
+ }
+
+ dirs.put(target.getParentFile(), new WriteTask(buildContext, target, cell.getValue()));
+ }
+ LOG.info("Sorted {} files into {} directories in {}", dirs.size(), dirs.keySet().size(), sw);
+
+ // Step three: submit parent directory creation tasks (via parallelStream()) and wait for them to complete
+ sw.reset().start();
+ dirs.keySet().parallelStream().forEach(path -> {
+ try {
+ Files.createDirectories(path.toPath());
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to create " + path, e);
+ }
+ });
+ LOG.debug("Parent directories created in {}", sw);
+
+ // Step four: submit all code generation tasks (via parallelStream()) and wait for them to complete
+ sw.reset().start();
+ final List<File> result = dirs.values().parallelStream()
+ .map(WriteTask::generateFile)
+ .collect(Collectors.toList());
+ LOG.debug("Generated {} files in {}", result.size(), sw);
+
+ return result;
+ }
+
+ private File persistentPath(final GeneratedFileType fileType) throws FileGeneratorException {
+ final File existing = persistentDirs.get(fileType);
+ if (existing != null) {
+ return existing;
+ }
+ final File newDir = persistentDirectory(fileType);
+ verify(persistentDirs.put(fileType, newDir) == null);
+ return newDir;
+ }
+
+ private File transientPath(final GeneratedFileType fileType) throws FileGeneratorException {
+ final File existing = transientDirs.get(fileType);
+ if (existing != null) {
+ return existing;
+ }
+
+ final File newDir = transientDirectory(fileType);
+ verify(transientDirs.put(fileType, newDir) == null);
+ return newDir;
+ }
+
+ private File persistentDirectory(final GeneratedFileType fileType) throws FileGeneratorException {
+ final File ret;
+ if (GeneratedFileType.SOURCE.equals(fileType)) {
+ ret = new File(project.getBuild().getSourceDirectory());
+ } else if (GeneratedFileType.RESOURCE.equals(fileType)) {
+ ret = new File(new File(project.getBuild().getSourceDirectory()).getParentFile(), "resources");
+ } else {
+ throw new FileGeneratorException("Unknown generated file type " + fileType);
+ }
+ return ret;
+ }
+
+ private File transientDirectory(final GeneratedFileType fileType) throws FileGeneratorException {
+ final File ret;
+ if (GeneratedFileType.SOURCE.equals(fileType)) {
+ ret = transientDirectory("generated-sources");
+ project.addCompileSourceRoot(ret.toString());
+ } else if (GeneratedFileType.RESOURCE.equals(fileType)) {
+ ret = transientDirectory("generated-resources");
+ project.addResource(createResouce(ret));
+ } else {
+ throw new FileGeneratorException("Unknown generated file type " + fileType);
+ }
+ return ret;
+ }
+
+ private File transientDirectory(final String component) {
+ return new File(buildDir, subdirFileName(component));
+ }
+
+ private String subdirFileName(final String component) {
+ return component + File.separatorChar + suffix;
+ }
+
+ private static Resource createResouce(final File directory) {
+ final Resource ret = new Resource();
+ ret.setDirectory(directory.toString());
+ return ret;
+ }
+
+ private static final class WriteTask {
+ private final BuildContext buildContext;
+ private final GeneratedFile file;
+ private final File target;
+
+ WriteTask(final BuildContext buildContext, final File target, final GeneratedFile file) {
+ this.buildContext = requireNonNull(buildContext);
+ this.target = requireNonNull(target);
+ this.file = requireNonNull(file);
+ }
+
+ File generateFile() {
+ try (OutputStream stream = buildContext.newFileOutputStream(target)) {
+ file.writeBody(stream);
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to generate file " + target, e);
+ }
+ return target;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.plugin;
+
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.apache.maven.project.MavenProject;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorFactory;
+
+/**
+ * Bridge to a {@link FileGenerator} instance.
+ *
+ * @author Robert Varga
+ */
+final class FileGeneratorTaskFactory extends GeneratorTaskFactory implements Identifiable<String> {
+ private final FileGeneratorArg arg;
+ private final FileGenerator gen;
+
+ private FileGeneratorTaskFactory(final FileGenerator gen, final FileGeneratorArg arg) {
+ super(gen.importResolutionMode());
+ this.arg = arg;
+ this.gen = gen;
+ }
+
+ static FileGeneratorTaskFactory of(final FileGeneratorFactory factory, final FileGeneratorArg arg)
+ throws FileGeneratorException {
+ return new FileGeneratorTaskFactory(factory.newFileGenerator(arg.getConfiguration()), arg);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return arg.getIdentifier();
+ }
+
+ @Override
+ FileGeneratorTask createTask(final MavenProject project, final ContextHolder context) {
+ return new FileGeneratorTask(this, context, project);
+ }
+
+ @Override
+ FileGenerator generator() {
+ return gen;
+ }
+
+ @Override
+ ToStringHelper addToStringProperties(final ToStringHelper helper) {
+ return super.addToStringProperties(helper).add("argument", arg);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+import org.sonatype.plexus.build.incremental.BuildContext;
+
+@NonNullByDefault
+abstract class GeneratorTask<T extends GeneratorTaskFactory> extends ParserModeAware {
+ private final ContextHolder context;
+ private final T factory;
+
+ GeneratorTask(final T factory, final ContextHolder context) {
+ this.factory = requireNonNull(factory);
+ this.context = requireNonNull(context);
+ }
+
+ @Override
+ final StatementParserMode parserMode() {
+ return factory.parserMode();
+ }
+
+ final Collection<File> execute(final BuildContext buildContext) throws FileGeneratorException, IOException {
+ return execute(factory, context, buildContext);
+ }
+
+ abstract Collection<File> execute(T factory, ContextHolder modelContext, BuildContext buildContext)
+ throws FileGeneratorException, IOException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.plugin;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator.ImportResolutionMode;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+
+@NonNullByDefault
+abstract class GeneratorTaskFactory extends ParserModeAware {
+ private final StatementParserMode parserMode;
+
+ GeneratorTaskFactory(final ImportResolutionMode importMode) {
+ switch (importMode) {
+ case REVISION_EXACT_OR_LATEST:
+ parserMode = StatementParserMode.DEFAULT_MODE;
+ break;
+ case SEMVER_LATEST:
+ parserMode = StatementParserMode.SEMVER_MODE;
+ break;
+ default:
+ throw new LinkageError("Unhandled import mode " + importMode);
+ }
+ }
+
+ @Override
+ final StatementParserMode parserMode() {
+ return parserMode;
+ }
+
+ final String generatorName() {
+ return generator().getClass().getName();
+ }
+
+ /**
+ * Create a new {@link GeneratorTask} which will work in scope of specified {@link MavenProject} with the effective
+ * model held in specified {@link ContextHolder}.
+ *
+ * @param project current Maven Project
+ * @param context model generation context
+ */
+ abstract GeneratorTask<?> createTask(MavenProject project, ContextHolder context);
+
+ abstract Object generator();
+
+ ToStringHelper addToStringProperties(final ToStringHelper helper) {
+ return helper.add("generator", generatorName());
+ }
+
+ @Override
+ public final String toString() {
+ return addToStringProperties(MoreObjects.toStringHelper(this).omitNullValues()).toString();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang2sources.plugin;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
+
+@NonNullByDefault
+abstract class ParserModeAware {
+
+ abstract StatementParserMode parserMode();
+}
private YangParser parser;
ProcessorModuleReactor(final YangParser parser, final Collection<YangTextSchemaSource> modelsInProject,
- final Collection<ScannedDependency> dependencies) {
+ final Collection<ScannedDependency> dependencies) {
this.parser = requireNonNull(parser);
this.modelsInProject = Maps.uniqueIndex(modelsInProject, YangTextSchemaSource::getIdentifier);
this.dependencies = ImmutableList.copyOf(dependencies);
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
+import org.opendaylight.yangtools.plugin.generator.api.FileGenerator;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.repo.api.StatementParserMode;
import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
import org.sonatype.plexus.build.incremental.BuildContext;
@Parameter(required = false)
private CodeGeneratorArg[] codeGenerators;
+ /**
+ * {@link FileGenerator} instances resolved via ServiceLoader can hold additional configuration, which details
+ * how they are executed.
+ */
+ @Parameter(required = false)
+ private FileGeneratorArg[] fileGenerators;
+
/**
* Source directory that will be recursively searched for yang files (ending
* with .yang suffix).
@Parameter(property = "yang.skip")
private String yangSkip;
+ @Parameter(defaultValue = "DEFAULT_MODE")
+ @Deprecated(forRemoval = true)
+ private StatementParserMode parserMode;
+
public YangToSourcesMojo() {
}
Util.checkClasspath(project, repoSystem, localRepository, remoteRepos);
if (yangToSourcesProcessor == null) {
- List<CodeGeneratorArg> codeGeneratorArgs = processCodeGenerators(codeGenerators);
-
// defaults to ${basedir}/src/main/yang
File yangFilesRootFile = processYangFilesRootDir(yangFilesRootDir, project.getBasedir());
Collection<File> excludedFiles = processExcludeFiles(excludeFiles, yangFilesRootFile);
yangToSourcesProcessor = new YangToSourcesProcessor(buildContext, yangFilesRootFile,
- excludedFiles, codeGeneratorArgs, project, inspectDependencies);
+ excludedFiles, arrayToList(codeGenerators), arrayToList(fileGenerators), project,
+ inspectDependencies);
}
yangToSourcesProcessor.conditionalExecute("true".equals(yangSkip));
}
- private static List<CodeGeneratorArg> processCodeGenerators(final CodeGeneratorArg[] codeGenerators) {
- List<CodeGeneratorArg> codeGeneratorArgs;
- if (codeGenerators == null) {
- codeGeneratorArgs = Collections.emptyList();
- } else {
- codeGeneratorArgs = Arrays.asList(codeGenerators);
- }
- return codeGeneratorArgs;
+ private static <T> List<T> arrayToList(final T[] array) {
+ return array == null ? ImmutableList.of() : Arrays.asList(array);
}
private static File processYangFilesRootDir(final String yangFilesRootDir, final File baseDir) {
return Collections2.transform(Arrays.asList(excludeFiles), f -> new File(baseDir, f));
}
-
}
*/
package org.opendaylight.yangtools.yang2sources.plugin;
-import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Maps;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Comparator;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorException;
+import org.opendaylight.yangtools.plugin.generator.api.FileGeneratorFactory;
import org.opendaylight.yangtools.yang.common.YangConstants;
import org.opendaylight.yangtools.yang.model.parser.api.YangParser;
import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRSchemaSource;
import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToIRTransformer;
import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
-import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
-import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator.ImportResolutionMode;
-import org.opendaylight.yangtools.yang2sources.spi.BuildContextAware;
-import org.opendaylight.yangtools.yang2sources.spi.MavenProjectAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonatype.plexus.build.incremental.BuildContext;
private final File yangFilesRootDir;
private final Set<File> excludedFiles;
private final List<CodeGeneratorArg> codeGeneratorArgs;
+ private final Map<String, FileGeneratorArg> fileGeneratorArgs;
private final MavenProject project;
private final boolean inspectDependencies;
private final BuildContext buildContext;
private final YangProvider yangProvider;
private YangToSourcesProcessor(final BuildContext buildContext, final File yangFilesRootDir,
- final Collection<File> excludedFiles, final List<CodeGeneratorArg> codeGenerators,
+ final Collection<File> excludedFiles, final List<CodeGeneratorArg> codeGeneratorArgs,
+ final List<FileGeneratorArg> fileGeneratorsArgs,
final MavenProject project, final boolean inspectDependencies, final YangProvider yangProvider) {
this.buildContext = requireNonNull(buildContext, "buildContext");
this.yangFilesRootDir = requireNonNull(yangFilesRootDir, "yangFilesRootDir");
this.excludedFiles = ImmutableSet.copyOf(excludedFiles);
- this.codeGeneratorArgs = ImmutableList.copyOf(codeGenerators);
+ this.codeGeneratorArgs = ImmutableList.copyOf(codeGeneratorArgs);
+ this.fileGeneratorArgs = Maps.uniqueIndex(fileGeneratorsArgs, FileGeneratorArg::getIdentifier);
this.project = requireNonNull(project);
this.inspectDependencies = inspectDependencies;
this.yangProvider = requireNonNull(yangProvider);
YangToSourcesProcessor(final File yangFilesRootDir, final Collection<File> excludedFiles,
final List<CodeGeneratorArg> codeGenerators, final MavenProject project, final boolean inspectDependencies,
final YangProvider yangProvider) {
- this(new DefaultBuildContext(), yangFilesRootDir, excludedFiles, codeGenerators, project,
- inspectDependencies, yangProvider);
+ this(new DefaultBuildContext(), yangFilesRootDir, excludedFiles, codeGenerators, ImmutableList.of(),
+ project, inspectDependencies, yangProvider);
}
YangToSourcesProcessor(final BuildContext buildContext, final File yangFilesRootDir,
final Collection<File> excludedFiles, final List<CodeGeneratorArg> codeGenerators,
- final MavenProject project, final boolean inspectDependencies) {
- this(buildContext, yangFilesRootDir, excludedFiles, codeGenerators, project, inspectDependencies,
- YangProvider.getInstance());
+ final List<FileGeneratorArg> fileGenerators, final MavenProject project,
+ final boolean inspectDependencies) {
+ this(buildContext, yangFilesRootDir, excludedFiles, codeGenerators, fileGenerators, project,
+ inspectDependencies, YangProvider.getInstance());
}
public void execute() throws MojoExecutionException, MojoFailureException {
}
// We need to instantiate all code generators to determine required import resolution mode
- final List<Entry<CodeGeneratorArg, BasicCodeGenerator>> codeGenerators = instantiateGenerators();
- final StatementParserMode importMode = determineRequiredImportMode(codeGenerators);
- final Optional<ProcessorModuleReactor> optReactor = createReactor(importMode, yangFilesInProject);
- if (!optReactor.isPresent()) {
+ final List<GeneratorTaskFactory> codeGenerators = instantiateGenerators();
+ if (codeGenerators.isEmpty()) {
+ LOG.warn("{} No code generators provided", LOG_PREFIX);
return;
}
- final ProcessorModuleReactor reactor = optReactor.get();
- if (!skip) {
- final Stopwatch watch = Stopwatch.createStarted();
- final ContextHolder holder;
+ final Set<StatementParserMode> parserModes = codeGenerators.stream()
+ .map(GeneratorTaskFactory::parserMode)
+ .collect(Collectors.toUnmodifiableSet());
+
+ // FIXME: store these files into state, so that we can verify/clean up
+ final Builder<File> files = ImmutableSet.builder();
+ for (StatementParserMode parserMode : parserModes) {
+ final Optional<ProcessorModuleReactor> optReactor = createReactor(yangFilesInProject, parserMode);
+ if (optReactor.isPresent()) {
+ final ProcessorModuleReactor reactor = optReactor.orElseThrow();
+
+ if (!skip) {
+ final Stopwatch watch = Stopwatch.createStarted();
+ final ContextHolder holder;
+
+ try {
+ holder = reactor.toContext();
+ } catch (YangParserException e) {
+ throw new MojoFailureException("Failed to process reactor " + reactor, e);
+ } catch (IOException e) {
+ throw new MojoExecutionException("Failed to read reactor " + reactor, e);
+ }
+
+ LOG.info("{} {} YANG models processed in {}", LOG_PREFIX, holder.getContext().getModules().size(),
+ watch);
+ files.addAll(generateSources(holder, codeGenerators, parserMode));
+ } else {
+ LOG.info("{} Skipping YANG code generation because property yang.skip is true", LOG_PREFIX);
+ }
- try {
- holder = reactor.toContext();
- } catch (YangParserException e) {
- throw new MojoFailureException("Failed to process reactor " + reactor, e);
- } catch (IOException e) {
- throw new MojoExecutionException("Failed to read reactor " + reactor, e);
+ // FIXME: this is not right: we should be generating the models exactly once!
+ // add META_INF/yang
+ final Collection<YangTextSchemaSource> models = reactor.getModelsInProject();
+ try {
+ yangProvider.addYangsToMetaInf(project, models);
+ } catch (IOException e) {
+ throw new MojoExecutionException("Failed write model files for " + models, e);
+ }
}
-
- LOG.info("{} {} YANG models processed in {}", LOG_PREFIX, holder.getContext().getModules().size(), watch);
- generateSources(holder, codeGenerators);
- } else {
- LOG.info("{} Skipping YANG code generation because property yang.skip is true", LOG_PREFIX);
- }
-
- // add META_INF/yang
- final Collection<YangTextSchemaSource> models = reactor.getModelsInProject();
- try {
- yangProvider.addYangsToMetaInf(project, models);
- } catch (IOException e) {
- throw new MojoExecutionException("Failed write model files for " + models, e);
}
// add META_INF/services
META_INF_YANG_SERVICES_STRING_JAR);
}
- private static StatementParserMode determineRequiredImportMode(
- final Collection<Entry<CodeGeneratorArg, BasicCodeGenerator>> codeGenerators) throws MojoFailureException {
- ImportResolutionMode requestedMode = null;
- BasicCodeGenerator requestingGenerator = null;
-
- for (Entry<CodeGeneratorArg, BasicCodeGenerator> entry : codeGenerators) {
- final BasicCodeGenerator generator = entry.getValue();
- final ImportResolutionMode mode = generator.getImportResolutionMode();
- if (mode == null) {
- // the generator does not care about the mode
- continue;
- }
-
- if (requestedMode == null) {
- // No mode yet, we have just determined it
- requestedMode = mode;
- requestingGenerator = generator;
- continue;
- }
-
- if (mode != requestedMode) {
- throw new MojoFailureException(String.format(
- "Import resolution mode conflict between %s (%s) and %s (%s)", requestingGenerator, requestedMode,
- generator, mode));
- }
- }
-
- if (requestedMode == null) {
- return StatementParserMode.DEFAULT_MODE;
- }
- switch (requestedMode) {
- case REVISION_EXACT_OR_LATEST:
- return StatementParserMode.DEFAULT_MODE;
- case SEMVER_LATEST:
- return StatementParserMode.SEMVER_MODE;
- default:
- throw new IllegalStateException("Unhandled import resolution mode " + requestedMode);
+ private List<GeneratorTaskFactory> instantiateGenerators() throws MojoExecutionException, MojoFailureException {
+ final List<GeneratorTaskFactory> generators = new ArrayList<>(codeGeneratorArgs.size());
+ for (CodeGeneratorArg arg : codeGeneratorArgs) {
+ generators.add(CodeGeneratorTaskFactory.create(arg));
+ LOG.info("{} Code generator instantiated from {}", LOG_PREFIX, arg.getCodeGeneratorClass());
}
- }
- private List<Entry<CodeGeneratorArg, BasicCodeGenerator>> instantiateGenerators() throws MojoExecutionException {
- final List<Entry<CodeGeneratorArg, BasicCodeGenerator>> generators = new ArrayList<>(codeGeneratorArgs.size());
- for (CodeGeneratorArg arg : codeGeneratorArgs) {
- arg.check();
+ // Search for available FileGenerator implementations
+ final Map<String, FileGeneratorFactory> factories = Maps.uniqueIndex(
+ ServiceLoader.load(FileGeneratorFactory.class), FileGeneratorFactory::getIdentifier);
+
+ // Assign instantiate FileGenerators with appropriate configurate
+ for (Entry<String, FileGeneratorFactory> entry : factories.entrySet()) {
+ final String id = entry.getKey();
+ FileGeneratorArg arg = fileGeneratorArgs.get(id);
+ if (arg == null) {
+ LOG.debug("{} No configuration for {}, using empty", LOG_PREFIX, id);
+ arg = new FileGeneratorArg(id);
+ }
- final BasicCodeGenerator generator;
try {
- generator = getInstance(arg.getCodeGeneratorClass(), BasicCodeGenerator.class);
- } catch (ReflectiveOperationException e) {
- throw new MojoExecutionException("Failed to instantiate code generator "
- + arg.getCodeGeneratorClass(), e);
+ generators.add(FileGeneratorTaskFactory.of(entry.getValue(), arg));
+ } catch (FileGeneratorException e) {
+ throw new MojoExecutionException("File generator " + id + " failed", e);
}
-
- LOG.info("{} Code generator instantiated from {}", LOG_PREFIX, arg.getCodeGeneratorClass());
- generators.add(new SimpleImmutableEntry<>(arg, generator));
+ LOG.info("{} Code generator {} instantiated", LOG_PREFIX, id);
}
return generators;
}
@SuppressWarnings("checkstyle:illegalCatch")
- private Optional<ProcessorModuleReactor> createReactor(final StatementParserMode parserMode,
- final List<File> yangFilesInProject) throws MojoExecutionException {
+ private Optional<ProcessorModuleReactor> createReactor(final List<File> yangFilesInProject,
+ final StatementParserMode parserMode) throws MojoExecutionException {
LOG.info("{} Inspecting {}", LOG_PREFIX, yangFilesRootDir);
- try {
- final Collection<File> allFiles = new ArrayList<>(yangFilesInProject);
- final Collection<ScannedDependency> dependencies;
- if (inspectDependencies) {
- dependencies = new ArrayList<>();
- final Stopwatch watch = Stopwatch.createStarted();
+ final Collection<File> allFiles = new ArrayList<>(yangFilesInProject);
+ final Collection<ScannedDependency> dependencies;
+ if (inspectDependencies) {
+ dependencies = new ArrayList<>();
+ final Stopwatch watch = Stopwatch.createStarted();
+
+ try {
ScannedDependency.scanDependencies(project).forEach(dep -> {
allFiles.add(dep.file());
dependencies.add(dep);
});
- LOG.info("{} Found {} dependencies in {}", LOG_PREFIX, dependencies.size(), watch);
- } else {
- dependencies = ImmutableList.of();
- }
-
- /*
- * Check if any of the listed files changed. If no changes occurred,
- * simply return null, which indicates and of execution.
- */
- final Stopwatch watch = Stopwatch.createStarted();
- if (!allFiles.stream().anyMatch(buildContext::hasDelta)) {
- LOG.info("{} None of {} input files changed", LOG_PREFIX, allFiles.size());
- return Optional.empty();
+ } catch (IOException e) {
+ LOG.error("{} Failed to scan dependencies", LOG_PREFIX, e);
+ throw new MojoExecutionException(LOG_PREFIX + " Failed to scan dependencies ", e);
}
+ LOG.info("{} Found {} dependencies in {}", LOG_PREFIX, dependencies.size(), watch);
+ } else {
+ dependencies = ImmutableList.of();
+ }
- final YangParser parser = parserFactory.createParser(parserMode);
- final List<YangTextSchemaSource> sourcesInProject = new ArrayList<>(yangFilesInProject.size());
+ /*
+ * Check if any of the listed files changed. If no changes occurred, simply return empty, which indicates
+ * end of execution.
+ */
+ final Stopwatch watch = Stopwatch.createStarted();
+ if (!allFiles.stream().anyMatch(buildContext::hasDelta)) {
+ LOG.info("{} None of {} input files changed", LOG_PREFIX, allFiles.size());
+ return Optional.empty();
+ }
+ try {
final List<Entry<YangTextSchemaSource, IRSchemaSource>> parsed = yangFilesInProject.parallelStream()
.map(file -> {
final YangTextSchemaSource textSource = YangTextSchemaSource.forFile(file);
try {
- return new SimpleImmutableEntry<>(textSource,
- TextToIRTransformer.transformText(textSource));
+ return Map.entry(textSource,TextToIRTransformer.transformText(textSource));
} catch (YangSyntaxErrorException | IOException e) {
throw new IllegalArgumentException("Failed to parse " + file, e);
}
})
.collect(Collectors.toList());
+ // FIXME: all of the above checks need to be done just once
+
+
+ final List<YangTextSchemaSource> sourcesInProject = new ArrayList<>(yangFilesInProject.size());
+ final YangParser parser = parserFactory.createParser(parserMode);
for (final Entry<YangTextSchemaSource, IRSchemaSource> entry : parsed) {
final YangTextSchemaSource textSource = entry.getKey();
final IRSchemaSource astSource = entry.getValue();
/**
* Call generate on every generator from plugin configuration.
*/
- @SuppressWarnings("checkstyle:illegalCatch")
- private void generateSources(final ContextHolder context,
- final Collection<Entry<CodeGeneratorArg, BasicCodeGenerator>> generators) throws MojoFailureException {
- if (generators.isEmpty()) {
- LOG.warn("{} No code generators provided", LOG_PREFIX);
- return;
- }
+ private Set<File> generateSources(final ContextHolder context, final Collection<GeneratorTaskFactory> generators,
+ final StatementParserMode parserMode) throws MojoFailureException {
+ final Builder<File> allFiles = ImmutableSet.builder();
+ for (GeneratorTaskFactory factory : generators) {
+ if (!parserMode.equals(factory.parserMode())) {
+ continue;
+ }
- final Map<String, String> thrown = new HashMap<>();
- for (Entry<CodeGeneratorArg, BasicCodeGenerator> entry : generators) {
- final String codeGeneratorClass = entry.getKey().getCodeGeneratorClass();
+ final Stopwatch sw = Stopwatch.createStarted();
+ final GeneratorTask<?> task = factory.createTask(project, context);
+ LOG.debug("{} Task {} initialized in {}", LOG_PREFIX, task, sw);
+ final Collection<File> files;
try {
- generateSourcesWithOneGenerator(context, entry.getKey(), entry.getValue());
- } catch (Exception e) {
- // try other generators, exception will be thrown after
- LOG.error("{} Unable to generate sources with {} generator", LOG_PREFIX, codeGeneratorClass, e);
- thrown.put(codeGeneratorClass, e.getClass().getCanonicalName());
+ files = task.execute(buildContext);
+ } catch (FileGeneratorException | IOException e) {
+ throw new MojoFailureException(LOG_PREFIX + " Generator " + factory + " failed", e);
}
- }
- if (!thrown.isEmpty()) {
- LOG.error("{} One or more code generators failed, including failed list(generatorClass=exception) {}",
- LOG_PREFIX, thrown);
- throw new MojoFailureException(LOG_PREFIX
- + " One or more code generators failed, including failed list(generatorClass=exception) " + thrown);
+ LOG.debug("{} Sources generated by {}: {}", LOG_PREFIX, factory.generatorName(), files);
+ LOG.info("{} Sources generated by {}: {} in {}", LOG_PREFIX, factory.generatorName(),
+ files == null ? 0 : files.size(), sw);
+ allFiles.addAll(files);
}
- }
-
- /**
- * Complete initialization of a code generator and invoke it.
- */
- private void generateSourcesWithOneGenerator(final ContextHolder context, final CodeGeneratorArg codeGeneratorCfg,
- final BasicCodeGenerator codeGenerator) throws IOException {
- final File outputDir = requireNonNull(codeGeneratorCfg.getOutputBaseDir(project),
- "outputBaseDir is null. Please provide a valid outputBaseDir value in pom.xml");
-
- project.addCompileSourceRoot(outputDir.getAbsolutePath());
-
- LOG.info("{} Sources will be generated to {}", LOG_PREFIX, outputDir);
- LOG.debug("{} Project root dir is {}", LOG_PREFIX, project.getBasedir());
- LOG.debug("{} Additional configuration picked up for : {}: {}", LOG_PREFIX, codeGeneratorCfg
- .getCodeGeneratorClass(), codeGeneratorCfg.getAdditionalConfiguration());
-
- if (codeGenerator instanceof BuildContextAware) {
- ((BuildContextAware)codeGenerator).setBuildContext(buildContext);
- }
- if (codeGenerator instanceof MavenProjectAware) {
- ((MavenProjectAware)codeGenerator).setMavenProject(project);
- }
- codeGenerator.setAdditionalConfig(codeGeneratorCfg.getAdditionalConfiguration());
-
- File resourceBaseDir = codeGeneratorCfg.getResourceBaseDir(project);
- YangProvider.setResource(resourceBaseDir, project);
- codeGenerator.setResourceBaseDir(resourceBaseDir);
- LOG.debug("{} Folder: {} marked as resources for generator: {}", LOG_PREFIX, resourceBaseDir,
- codeGeneratorCfg.getCodeGeneratorClass());
-
- if (outputDir.exists()) {
- Files.walk(outputDir.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
- LOG.info("{} Succesfully deleted output directory {}", LOG_PREFIX, outputDir);
- }
- final Stopwatch watch = Stopwatch.createStarted();
- Collection<File> generated = codeGenerator.generateSources(context.getContext(), outputDir,
- context.getYangModules(), context);
-
- LOG.debug("{} Sources generated by {}: {}", LOG_PREFIX, codeGeneratorCfg.getCodeGeneratorClass(), generated);
- LOG.info("{} Sources generated by {}: {} in {}", LOG_PREFIX, codeGeneratorCfg.getCodeGeneratorClass(),
- generated == null ? 0 : generated.size(), watch);
- }
-
- /**
- * Instantiate object from fully qualified class name.
- */
- private static <T> T getInstance(final String codeGeneratorClass, final Class<T> baseType)
- throws ReflectiveOperationException {
- final Class<?> clazz = Class.forName(codeGeneratorClass);
- checkArgument(baseType.isAssignableFrom(clazz), "Code generator %s has to implement %s", clazz, baseType);
- return baseType.cast(clazz.getConstructor().newInstance());
+ return allFiles.build();
}
}
*/
package org.opendaylight.yangtools.yang2sources.plugin;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.ArrayList;
import org.apache.maven.model.Build;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@Test
public void yangToSourceMojoTest() throws Exception {
- Mockito.when(this.project.getPlugin(YangToSourcesMojo.PLUGIN_NAME)).thenReturn(this.plugin);
+ doReturn(plugin).when(project).getPlugin(YangToSourcesMojo.PLUGIN_NAME);
this.mojo = new YangToSourcesMojo();
- this.mojo.setProject(this.project);
+ this.mojo.setProject(project);
this.mojo.buildContext = new DefaultBuildContext();
this.mojo.execute();
- Assert.assertNotNull(this.mojo);
+ assertNotNull(this.mojo);
final YangToSourcesProcessor processor = Mockito.mock(YangToSourcesProcessor.class);
this.mojo = new YangToSourcesMojo(processor);
- this.mojo.setProject(this.project);
+ this.mojo.setProject(project);
this.mojo.execute();
- Mockito.verify(processor).conditionalExecute(false);
+ verify(processor).conditionalExecute(false);
}
@Test
public void test() throws Exception {
prepareProcessor();
- Assert.assertNotNull(this.proc);
- this.mojo = new YangToSourcesMojo(this.proc);
- this.mojo.setProject(this.project);
+ assertNotNull(proc);
+ this.mojo = new YangToSourcesMojo(proc);
+ this.mojo.setProject(project);
this.mojo.execute();
- Assert.assertNotNull(this.mojo);
+ assertNotNull(mojo);
}
private void prepareProcessor() {
final CodeGeneratorArg codeGeneratorArg = new CodeGeneratorArg(GeneratorMock.class.getName(),
"target/YangToSourcesMojoTest-outputBaseDir");
codeGenerators.add(codeGeneratorArg);
- final MavenProject mvnProject = Mockito.mock(MavenProject.class);
final Build build = new Build();
- Mockito.when(mvnProject.getBuild()).thenReturn(build);
+ build.setDirectory("testDir");
+ doReturn(build).when(project).getBuild();
final boolean dependencies = true;
this.proc = new YangToSourcesProcessor(file, ImmutableList.of(excludedYang), codeGenerators,
- mvnProject, dependencies, YangProvider.getInstance());
+ project, dependencies, YangProvider.getInstance());
}
}
*/
package org.opendaylight.yangtools.yang2sources.plugin;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.doReturn;
+
import com.google.common.collect.ImmutableList;
import java.io.File;
import java.util.List;
import org.apache.maven.model.Build;
import org.apache.maven.project.MavenProject;
-import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
+import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
import org.opendaylight.yangtools.yang2sources.plugin.GenerateSourcesTest.GeneratorMock;
-@RunWith(MockitoJUnitRunner.Silent.class)
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class YangToSourcesProcessorTest {
+ @Mock
+ public File buildContext;
+ @Mock
+ public MavenProject project;
+ @Mock
+ public YangProvider inspectDependencies;
- private final File buildContext = Mockito.mock(File.class);
- private final List<File> yangFilesRootDir = ImmutableList.of(buildContext);
- private final MavenProject project = Mockito.mock(MavenProject.class);
private final boolean dep = false;
- private final YangProvider inspectDependencies = Mockito.mock(YangProvider.class);
+ private List<File> yangFilesRootDir;
+
+ @Before
+ public void before() {
+ yangFilesRootDir = ImmutableList.of(buildContext);
+ }
@Test
public void yangToSourcesProcessotTest() {
- Mockito.when(this.buildContext.getPath()).thenReturn("path");
YangToSourcesProcessor processor = new YangToSourcesProcessor(buildContext, yangFilesRootDir,
ImmutableList.of(), project, dep, inspectDependencies);
- Assert.assertNotNull(processor);
+ assertNotNull(processor);
processor = new YangToSourcesProcessor(buildContext, yangFilesRootDir, ImmutableList.of(), project, dep,
inspectDependencies);
- Assert.assertNotNull(processor);
+ assertNotNull(processor);
}
@Test
public void test() throws Exception {
final File file = new File(getClass().getResource("/yang").getFile());
final File excludedYang = new File(getClass().getResource("/yang/excluded-file.yang").getFile());
- final String path = file.getPath();
final CodeGeneratorArg codeGeneratorArg = new CodeGeneratorArg(GeneratorMock.class.getName(),
"target/YangToSourcesProcessorTest-outputBaseDir");
final List<CodeGeneratorArg> codeGenerators = ImmutableList.of(codeGeneratorArg);
- final MavenProject mvnProject = Mockito.mock(MavenProject.class);
final Build build = new Build();
- Mockito.when(mvnProject.getBuild()).thenReturn(build);
+ build.setDirectory("foo");
+ doReturn(build).when(project).getBuild();
final boolean dependencies = true;
final YangToSourcesProcessor proc = new YangToSourcesProcessor(file, ImmutableList.of(excludedYang),
- codeGenerators, mvnProject, dependencies, YangProvider.getInstance());
- Assert.assertNotNull(proc);
+ codeGenerators, project, dependencies, YangProvider.getInstance());
+ assertNotNull(proc);
proc.execute();
}
-
}