All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bytedeco.gradle.javacpp.BuildExtension Maven / Gradle / Ivy

/*
 * Copyright (C) 2020-2022 Samuel Audet
 *
 * Licensed either under the Apache License, Version 2.0, or (at your option)
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation (subject to the "Classpath" exception),
 * either version 2, or any later version (collectively, the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     http://www.gnu.org/licenses/
 *     http://www.gnu.org/software/classpath/license.html
 *
 * or as provided in the LICENSE.txt file that accompanied this code.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.bytedeco.gradle.javacpp;

import groovy.util.Node;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.XmlProvider;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ResolvedArtifact;
import org.gradle.api.artifacts.ResolvedDependency;
import org.gradle.api.internal.project.DefaultProject;
import org.gradle.api.internal.tasks.DefaultTaskDependencyFactory;
import org.gradle.api.internal.tasks.TaskDependencyFactory;
import org.gradle.api.plugins.BasePluginConvention;
import org.gradle.api.publish.maven.MavenArtifact;
import org.gradle.api.publish.maven.MavenPom;
import org.gradle.api.publish.maven.internal.artifact.FileBasedMavenArtifact;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Provides helper methods intended to be used with the "maven-publish" plugin.
 *
 * @author Samuel Audet
 */
public class BuildExtension {
    private static final Constructor compatibleArtifactConstructor;
    private static final boolean isLegacy;
    private final Logger logger = LoggerFactory.getLogger(BuildExtension.class);

    BuildPlugin plugin;
    Project project;

    static {
        boolean legacyCheck;
        Constructor compatibleConstructor;
        try {
            // If gradle version is lower than 6.2, use legacy constructor.
            compatibleConstructor = FileBasedMavenArtifact.class.getConstructor(File.class);
            legacyCheck = true;
        } catch (NoSuchMethodException e) {
            try {
                // If gradle version is equals or higher than 6.2, use latest constructor.
                compatibleConstructor = FileBasedMavenArtifact.class.getConstructor(File.class, TaskDependencyFactory.class);
                legacyCheck = false;
            } catch (NoSuchMethodException e2) {
                // If no compatible constructor found, constructor signature modified on latest version and do not compatible with this code.
                // Throw exception to prevent build.
                throw new RuntimeException("Could not find constructor for FileBasedMavenArtifact (Incompatible with this gradle version)", e);
            }
        }
        isLegacy = legacyCheck;
        compatibleArtifactConstructor = compatibleConstructor;
    }

    public BuildExtension(BuildPlugin plugin) {
        this.plugin = plugin;
        this.project = plugin.project;
    }

    /**
     * Copies artifacts that can be resolved from the configuration's dependencies, while
     * excluding the JAR file being built. With this, we can easily republish all JAR files
     * with classifiers at the same time, successfully working around Gradle's limitations.
     *
     * @param configuration containing dependencies to use
     * @return copies of existing artifacts
     * @throws java.io.IOException when JAR files cannot be copied into the project's build libs directory
     * @see https://github.com/gradle/gradle/issues/2882
     */
    public List existingArtifacts(Configuration configuration) throws IOException {
        ArrayList artifacts = new ArrayList();
        BasePluginConvention bc = project.getConvention().getPlugin(BasePluginConvention.class);
        File libsDir = new File(project.getBuildDir(), bc.getLibsDirName());
        libsDir.mkdirs();
        try {
            // Temporarily rename our project to prevent Gradle from resolving the artifacts to project dependencies without files.
            Field nameField = DefaultProject.class.getDeclaredField("name");
            nameField.setAccessible(true);
            String name = (String)nameField.get(project);
            nameField.set(project, name + "-renamed");
            for (ResolvedDependency rd : configuration.getResolvedConfiguration().getLenientConfiguration().getFirstLevelModuleDependencies()) {
                if (rd.getModuleGroup().equals(project.getGroup()) && rd.getModuleName().equals(name)) {
                    for (ResolvedArtifact ra : rd.getModuleArtifacts()) {
                        if (ra.getClassifier() != null && !ra.getClassifier().equals(plugin.getPlatform() + plugin.getPlatformExtension())) {
                            try {
                                File in = ra.getFile();
                                File out = new File(libsDir, in.getName());
                                Files.copy(in.toPath(), out.toPath(), StandardCopyOption.REPLACE_EXISTING);
                                MavenArtifact ma = isLegacy ? compatibleArtifactConstructor.newInstance(out) :
                                        compatibleArtifactConstructor.newInstance(out, DefaultTaskDependencyFactory.withNoAssociatedProject());
                                ma.setClassifier(ra.getClassifier());
                                artifacts.add(ma);
                            } catch (RuntimeException e) {
                                // probably ArtifactNotFoundException -> ignore
                            }
                        }
                    }
                }
            }
            nameField.set(project, name);
        } catch (ReflectiveOperationException e) {
            logger.warn("Could not get artifacts: " + e);
        }
        return artifacts;
    }

    /** Returns {@code xmlAction(configuration, null)}. */
    public Action xmlAction(Configuration configuration) {
        return xmlAction(configuration, null);
    }
    /**
     * Returns object to be called with {@link MavenPom#withXml(Action)} to create a pom.xml file for "-platform" artifacts.
     *
     * @param configuration containing dependencies to use
     * @param extension like "-gpl", "-gpu", "-python", etc (optional)
     * @return an {@link Action} that fills up an {@link XmlProvider}
     * @see PlatformPlugin
     * @see Reducing the Number of Dependencies
     */
    public Action xmlAction(final Configuration configuration, final String extension) {
        return new Action() { public void execute(XmlProvider xml) {
            String[] allPlatforms = {"android-arm", "android-arm64", "android-x86", "android-x86_64",
                                     "ios-arm", "ios-arm64", "ios-x86", "ios-x86_64",
                                     "linux-armhf", "linux-arm64", "linux-ppc64le", "linux-x86", "linux-x86_64",
                                     "macosx-arm64", "macosx-x86_64", "windows-x86", "windows-x86_64"};

            String[] osNameFrom = {"linux", "mac os x", "windows"};
            String[] osNameKernel = {"linux", "darwin", "windows"};
            String[] osNameType = {"name", "name", "family"};
            String[] osNameTo = {"linux", "macosx", "windows"};

            String[] osArchFrom = {"arm", "aarch64", "armv8", "ppc64le", "i386", "i486", "i586", "i686", "amd64", "x86-64"};
            String[] osArchTo = {"armhf", "arm64", "arm64", "ppc64le", "x86", "x86", "x86", "x86", "x86_64", "x86_64"};

            ArrayList platforms = new ArrayList();
            Node propertiesNode = xml.asNode().appendNode("properties");
            Node dependenciesNode = xml.asNode().appendNode("dependencies");
            for (ResolvedDependency rd : configuration.getResolvedConfiguration().getLenientConfiguration().getFirstLevelModuleDependencies()) {
                if (rd.getModuleGroup().equals(project.getGroup()) && rd.getModuleName().equals(project.getName())) {
                    Node dependencyNode = dependenciesNode.appendNode("dependency");
                    dependencyNode.appendNode("groupId", rd.getModuleGroup());
                    dependencyNode.appendNode("artifactId", rd.getModuleName());
                    dependencyNode.appendNode("version", rd.getModuleVersion());
                }
                for (ResolvedArtifact ra : rd.getModuleArtifacts()) {
                    Node dependencyNode = dependenciesNode.appendNode("dependency");
                    dependencyNode.appendNode("groupId", rd.getModuleGroup());
                    dependencyNode.appendNode("artifactId", rd.getModuleName());
                    dependencyNode.appendNode("version", rd.getModuleVersion());
                    if (ra.getClassifier() != null) {
                        String platform = ra.getClassifier();
                        if (extension != null && platform.endsWith(extension)) {
                            platform = platform.substring(0, platform.length() - extension.length());
                        }
                        dependencyNode.appendNode("classifier", "${javacpp.platform." + platform + "}");
                        platforms.add(platform);
                    }
                }
            }

            propertiesNode.appendNode("javacpp.platform.extension", extension != null ? extension : "");
            for (String platform : platforms) {
                propertiesNode.appendNode("javacpp.platform." + platform, platform + "${javacpp.platform.extension}");
            }

            Node profilesNode = xml.asNode().appendNode("profiles");
            Node profileNode = profilesNode.appendNode("profile");
            profileNode.appendNode("id", "javacpp-platform-default");
            profileNode.appendNode("activation").appendNode("property").appendNode("name", "!javacpp.platform");
            propertiesNode = profileNode.appendNode("properties");
            propertiesNode.appendNode("javacpp.platform", "${os.name}-${os.arch}");

            profileNode = profilesNode.appendNode("profile");
            profileNode.appendNode("id", "javacpp-platform-custom");
            profileNode.appendNode("activation").appendNode("property").appendNode("name", "javacpp.platform");
            propertiesNode = profileNode.appendNode("properties");
            for (String profilePlatform : platforms) {
                propertiesNode.appendNode("javacpp.platform." + profilePlatform, "${javacpp.platform}${javacpp.platform.extension}");
            }

            profileNode = profilesNode.appendNode("profile");
            profileNode.appendNode("id", "javacpp-platform-host");
            profileNode.appendNode("activation").appendNode("property").appendNode("name", "javacpp.platform.host");
            propertiesNode = profileNode.appendNode("properties");
            propertiesNode.appendNode("javacpp.platform", "${os.name}-${os.arch}${javacpp.platform.extension}");
            for (String profilePlatform : platforms) {
                propertiesNode.appendNode("javacpp.platform." + profilePlatform, "${os.name}-${os.arch}${javacpp.platform.extension}");
            }

            profileNode = profilesNode.appendNode("profile");
            profileNode.appendNode("id", "javacpp.platform.custom-true");
            profileNode.appendNode("activation").appendNode("property").appendNode("name", "javacpp.platform.custom");
            propertiesNode = profileNode.appendNode("properties");
            propertiesNode.appendNode("javacpp.platform", "");
            for (String profilePlatform : platforms) {
                propertiesNode.appendNode("javacpp.platform." + profilePlatform, "");
            }

            profileNode = profilesNode.appendNode("profile");
            profileNode.appendNode("id", "javacpp-platform-none");
            profileNode.appendNode("activation").appendNode("property").appendNode("name", "javacpp.platform.none");
            propertiesNode = profileNode.appendNode("properties");
            propertiesNode.appendNode("javacpp.platform", "");
            for (String profilePlatform : platforms) {
                propertiesNode.appendNode("javacpp.platform." + profilePlatform, "");
            }

            for (String platform : platforms) {
                profileNode = profilesNode.appendNode("profile");
                profileNode.appendNode("id", "javacpp-platform-" + platform);
                Node activationPropertyNode = profileNode.appendNode("activation").appendNode("property");
                activationPropertyNode.appendNode("name", "javacpp.platform");
                activationPropertyNode.appendNode("value", platform);
                propertiesNode = profileNode.appendNode("properties");
                propertiesNode.appendNode("javacpp.platform", platform);
                for (String profilePlatform : platforms) {
                    propertiesNode.appendNode("javacpp.platform." + profilePlatform,
                            platform == profilePlatform ? "${javacpp.platform}${javacpp.platform.extension}" : "");
                }
            }

            // Profiles to modify the transitive dependencies when picked up from other pom.xml files, for example:
            // mvn -Djavacpp.platform.custom -Djavacpp.platform.host -Djavacpp.platform.linux-x86_64 -Djavacpp.platform.windows-x86_64 ...
            for (String platform : platforms) {
                profileNode = profilesNode.appendNode("profile");
                profileNode.appendNode("id", "javacpp.platform." + platform + "-true");
                profileNode.appendNode("activation").appendNode("property").appendNode("name", "javacpp.platform." + platform);
                profileNode.appendNode("properties").appendNode("javacpp.platform." + platform, platform + "${javacpp.platform.extension}");
            }

            for (int i = 0; i < osNameFrom.length; i++) {
                for (int j = 0; j < osArchFrom.length; j++) {
                    String[] osArchs = osArchFrom[j].equals(osArchTo[j]) || (j + 1 < osArchTo.length && osArchTo[j].equals(osArchTo[j + 1]))
                            ? new String[] { osArchFrom[j] }
                            : new String[] { osArchFrom[j], osArchTo[j] };
                    for (String osArch : osArchs) {
                        String platform = osNameTo[i] + "-" + osArchTo[j];
                        if (platforms.contains(platform)) {
                            profileNode = profilesNode.appendNode("profile");
                            profileNode.appendNode("id", "javacpp.platform.custom-" + osNameTo[i] + "-" + osArch);
                            Node activationNode = profileNode.appendNode("activation");
                            activationNode.appendNode("property").appendNode("name", "javacpp.platform.host");
                            Node osNode = activationNode.appendNode("os");
                            osNode.appendNode(osNameType[i], osNameFrom[i]);
                            osNode.appendNode("arch", osArch);
                            profileNode.appendNode("properties").appendNode("javacpp.platform." + platform, platform + "${javacpp.platform.extension}");
                        }
                    }
                }
            }

            // Profiles to set the default javacpp.platform property: If someone knows a better way to do this, please do let me know!
            for (int i = 0; i < osNameFrom.length; i++) {
                profileNode = profilesNode.appendNode("profile");
                profileNode.appendNode("id", osNameTo[i]);
                profileNode.appendNode("activation").appendNode("os").appendNode(osNameType[i], osNameFrom[i]);
                propertiesNode = profileNode.appendNode("properties");
                propertiesNode.appendNode("os.kernel", osNameKernel[i]);
                propertiesNode.appendNode("os.name", osNameTo[i]);
            }

            for (int i = 0; i < osArchFrom.length; i++) {
                if (!osArchFrom[i].equals(osArchTo[i])) {
                    profileNode = profilesNode.appendNode("profile");
                    profileNode.appendNode("id", osArchFrom[i]);
                    profileNode.appendNode("activation").appendNode("os").appendNode("arch", osArchFrom[i]);
                    profileNode.appendNode("properties").appendNode("os.arch", osArchTo[i]);
                }
            }
        }};
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy