com.github.wix_maven.LightMojo.java Source code

Java tutorial

Introduction

Here is the source code for com.github.wix_maven.LightMojo.java

Source

package com.github.wix_maven;

/*
 * #%L
 * WiX Toolset (Windows Installer XML) Maven Plugin
 * %%
 * Copyright (C) 2013 - 2014 GregDomjan NetIQ
 * %%
 * Licensed under the Apache License, Version 2.0 (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
 * 
 * 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.
 * #L%
 */

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
import org.apache.maven.shared.artifact.filter.collection.ClassifierFilter;
import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter;
import org.apache.maven.shared.artifact.filter.collection.TypeFilter;
import org.codehaus.plexus.compiler.util.scan.*;
import org.codehaus.plexus.compiler.util.scan.mapping.*;
import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
import org.codehaus.plexus.util.cli.Commandline;

import java.io.File;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Goal which executes WiX light to create a .msi file.
 * 
 * The following project dependency inclusion patterns apply<br>
 * Dependent Wixlib project 'Foo' with possible output redefined as 'bar' adds to commandline <br>
 * ${narunpack}\Foo-version\Bar.wixlib
 * 
 * @goal light
 * @phase package
 * @requiresProject true
 * @requiresDependencyResolution compile
 */
public class LightMojo extends AbstractLinker {

    /**
     * Re use cabinet files across multiple linkages. (-reusecab)
     * 
     * @parameter expression="${wix.reuseCab}" default-value="false"
     */
    private boolean reuseCabs;

    /**
     * Location of the WiX localization files.
     * 
     * @parameter expression="${localizationFiles}"
     */
    // private File[] localizationFiles;

    /**
     * Bind files, only useful wixout format
     * 
     * @parameter expression="${wix.bindFiles.msi}" default-value="false"
     */
    private boolean bindFiles;

    private void addLocaleOptions(Commandline cl, String culture) {
        // TODO: culture might be a list of primary and fallback cultures
        if (culture != null)
            cl.addArguments(new String[] { "-cultures:" + culture });
    }

    private void addReuseCabOptions(Commandline cl, String arch) {
        // TODO: culture might be a list of primary and fallback cultures
        if (reuseCabs) {
            File resolvedCabCacheDirectory = new File(cabCacheDirectory, arch); // TODO: provide pattern replace
            cl.addArguments(
                    new String[] { "-reusecab", "-cc", resolvedCabCacheDirectory.getAbsolutePath() + "\\" });
            if (!resolvedCabCacheDirectory.exists())
                resolvedCabCacheDirectory.mkdirs();
        }
    }

    private String outputExtension() {
        if (PACK_PATCH.equalsIgnoreCase(getPackaging())) { // final msp output is from pyro
            return "wixmsp";
        }
        // msi/msm extension - actual build differences are in xml
        return getPackageOutputExtension();
    }

    protected Set<Artifact> getDependencySets() throws MojoExecutionException {
        FilterArtifacts filter = new FilterArtifacts();
        filter.addFilter(new ProjectTransitivityFilter(project.getDependencyArtifacts(), true));
        filter.addFilter(new ClassifierFilter(null, "x86,x64,intel") {
            /*
             * (non-Javadoc)
             * 
             * @see org.apache.maven.plugin.dependency.utils.filters.AbstractArtifactFeatureFilter#compareFeatures(String,String)
             */

            protected boolean compareFeatures(String lhs, String rhs) {
                return lhs != null && lhs.startsWith(rhs);
            }
        });
        filter.addFilter(new TypeFilter("wixlib,msi,msp", null));

        // start with all artifacts.
        @SuppressWarnings("unchecked")
        Set<Artifact> artifacts = project.getArtifacts();

        // perform filtering
        try {
            artifacts = filter.filter(artifacts);
        } catch (ArtifactFilterException e) {
            throw new MojoExecutionException(e.getMessage(), e);
        }

        return artifacts;
    }

    @Override
    protected void multilink(File toolDirectory) throws MojoExecutionException {

        File linkTool = new File(toolDirectory, "/bin/light.exe");
        if (!linkTool.exists())
            throw new MojoExecutionException("Light tool doesn't exist " + linkTool.getAbsolutePath());

        defaultLocale();

        Set<Artifact> artifacts = getDependencySets();

        for (Iterator<Artifact> i = artifacts.iterator(); i.hasNext();) {
            Artifact libGroup = i.next();
            getLog().debug("Attempting to unpack resources for " + libGroup.toString());
            unpackResource(libGroup);
        }

        for (String arch : getPlatforms()) {
            for (String culture : culturespecs()) {

                File archOutputFile = getOutput(arch, culture, outputExtension());

                getLog().info(" -- Linking : " + archOutputFile.getPath());
                try {
                    // we are using source scanning to find all the files for the build - because all should be listed we don't check for just newer
                    // ones.
                    // TODO: add check to see if the msi is out of date compared to input files of all kinds.
                    SourceInclusionScanner scanner = new SimpleSourceInclusionScanner(getIncludes(), getExcludes());
                    scanner.addSourceMapping(new SingleTargetSourceMapping(".wixobj", archOutputFile.getName()));
                    scanner.addSourceMapping(new SingleTargetSourceMapping(".wixlib", archOutputFile.getName()));
                    @SuppressWarnings("unchecked")
                    Set<File> objects = scanner.getIncludedSources(getArchIntDirectory(arch), archOutputFile);
                    // **/{arch}/*.wixlib
                    // **/{arch}/*.wixobj

                    Set<String> allSourceRoots = new LinkedHashSet<String>(fileSourceRoots); // we need this to keep order for more specific locale
                    List<File> locales = null; // coming first
                    if (wxlInputDirectory.exists()) {
                        // culture might be a list of primary and fallback cultures
                        // include all the wxl files and the -culture option will sort them out.
                        // include the files from only the primary culture and the nuetral.
                        scanner = new SimpleSourceInclusionScanner(getLocaleIncludes(), getLocaleExcludes());
                        scanner.addSourceMapping(new SingleTargetSourceMapping(".wxl", archOutputFile.getName()));
                        // The order of -loc is currently (wix 3.7) important due to an issue with UI element
                        locales = asSortedList(scanner.getIncludedSources(wxlInputDirectory, archOutputFile));

                        addBinderOption(wxlInputDirectory, culture, allSourceRoots);
                    }

                    Set<String> objectFiles = new HashSet<String>();
                    if (!objects.isEmpty()) {
                        for (Iterator<File> i = objects.iterator(); i.hasNext();) {
                            objectFiles.add(getRelative(i.next()));
                        }
                    }
                    for (Iterator<Artifact> i = artifacts.iterator(); i.hasNext();) {
                        Artifact libGroup = i.next();
                        getLog().debug(libGroup.toString());
                        unpackResource(libGroup);
                        if (PACK_LIB.equalsIgnoreCase(libGroup.getType())) {
                            // try unpack resources
                            addResource(libGroup, culture, allSourceRoots);

                            Set<Artifact> depArtifacts = getRelatedArtifacts(libGroup, arch, culture);
                            for (Iterator<Artifact> j = depArtifacts.iterator(); j.hasNext();) {
                                Artifact lib = j.next();
                                objectFiles.add(getRelative(lib.getFile()));
                            }
                        }
                        if (PACK_INSTALL.equalsIgnoreCase(libGroup.getType())
                        //                        || PACK_PATCH.equalsIgnoreCase(libGroup.getType())
                        ) {
                            File resUnpackDirectory = new File(unpackDirectory,
                                    libGroup.getGroupId() + "-" + libGroup.getArtifactId());
                            if (resUnpackDirectory.exists())
                                allSourceRoots.add(resUnpackDirectory.getAbsolutePath());
                        }
                    }

                    if (!objectFiles.isEmpty()) {
                        Commandline cl = new Commandline();

                        cl.setExecutable(linkTool.getAbsolutePath());
                        cl.setWorkingDirectory(relativeBase);//                  wxsInputDirectory
                        addToolsetGeneralOptions(cl);

                        if (bindFiles)
                            cl.addArguments(new String[] { "-bf" });

                        cl.addArguments(new String[] { "-out", archOutputFile.getAbsolutePath() });

                        addOptions(cl, allSourceRoots);
                        addLocaleOptions(cl, culture);

                        if (VALIDATE_SUPPRESS.equalsIgnoreCase(validate)
                                || VALIDATE_UNIT.equalsIgnoreCase(validate)) {
                            cl.addArguments(new String[] { "-sval" });
                        }

                        if (locales != null) {
                            for (Iterator<File> i = locales.iterator(); i.hasNext();) {
                                cl.addArguments(new String[] { "-loc", getRelative(i.next()) });
                            }
                        }

                        addWixExtensions(cl);
                        addOtherOptions(cl);
                        addReuseCabOptions(cl, arch);

                        cl.addArguments(objectFiles.toArray(new String[0]));

                        if (!archOutputFile.getParentFile().exists())
                            archOutputFile.getParentFile().mkdirs();
                        link(cl);
                        // projectHelper.attachArtifact(project, packaging, classifier, archOutputFile);
                    }

                } catch (InclusionScanException e) {
                    throw new MojoExecutionException("Scanning for updated files failed", e);
                }

            }
        }
    }

    /**
     * Add neutral and optional culture specific binder folders
     * 
     * @param baseFolder
     * @param culture
     * @param allSourceRoots
     */
    private void addBinderOption(File baseFolder, String culture, Set<String> allSourceRoots) {
        if (culture != null) {
            // TODO: might be that all cultures should be added
            // TODO: might be that LanguageID should be added also
            File cultureWxlInputDirectory = new File(baseFolder, getPrimaryCulture(culture));
            if (cultureWxlInputDirectory.exists())
                allSourceRoots.add(cultureWxlInputDirectory.getAbsolutePath());
        }
        // order is important, prefer files in culture specific over same in neutral
        allSourceRoots.add(baseFolder.getAbsolutePath());
    }

    /**
     * Add resources attached from dependencies
     * 
     * @param libGroup
     * @param culture
     * @param allSourceRoots
     */
    private void addResource(Artifact libGroup, String culture, Set<String> allSourceRoots) {
        File resUnpackDirectory = new File(unpackDirectory, libGroup.getGroupId() + "-" + libGroup.getArtifactId());

        File neutralFolder = new File(resUnpackDirectory, "wix-locale");
        if (neutralFolder.exists()) {
            addBinderOption(neutralFolder, culture, allSourceRoots);
        } else {
            getLog().debug(String.format("Warning: %1$s:%2$s resources not unpacked", libGroup.getGroupId(),
                    libGroup.getArtifactId()));
        }
    }

    private void unpackResource(Artifact libGroup) {
        // TODO: support compile if( libGroup.getFile().isFile() )
        zipUnArchiver.setSourceFile(libGroup.getFile());
        File resUnpackDirectory = new File(unpackDirectory, libGroup.getGroupId() + "-" + libGroup.getArtifactId());
        //      zipUnArchiver.extract(subfolder, resUnpackDirectory);

        if (!resUnpackDirectory.exists())
            resUnpackDirectory.mkdirs();

        zipUnArchiver.setDestDirectory(resUnpackDirectory);
        IncludeExcludeFileSelector[] selectors = new IncludeExcludeFileSelector[] {
                new IncludeExcludeFileSelector() };
        selectors[0].setIncludes("wix-locale/**,cabs/**".split(","));
        zipUnArchiver.setFileSelectors(selectors);
        zipUnArchiver.extract();
    }

    /**
     * Based on Maven-dependency-plugin AbstractFromConfigurationMojo.
     * 
     * Resolves the Artifact from the remote repository if necessary. If no version is specified, it will be retrieved from the dependency list or
     * from the DependencyManagement section of the pom.
     * 
     * @param artifactItem
     *            containing information about artifact from plugin configuration.
     * @return Artifact object representing the specified file.
     * @throws MojoExecutionException
     *             with a message if the version can't be found in DependencyManagement.
     */
    protected Set<Artifact> getRelatedArtifacts(Artifact artifactItem, String arch, String culture)
            throws MojoExecutionException {

        Set<Artifact> artifactSet = new HashSet<Artifact>();

        // Map managedVersions = createManagedVersionMap( factory, project.getId(), project.getDependencyManagement() );
        VersionRange vr;
        try {
            vr = VersionRange.createFromVersionSpec(artifactItem.getVersion());
        } catch (InvalidVersionSpecificationException e1) {
            vr = VersionRange.createFromVersion(artifactItem.getVersion());
        }

        if (PACK_LIB.equalsIgnoreCase(artifactItem.getType())
                || PACK_MERGE.equalsIgnoreCase(artifactItem.getType())) {
            boolean hasSomething = true;
            // even if this module has culture it's base modules may be neutral
            try {
                String classifier = arch + "-" + "neutral";
                getArtifact(artifactItem.getGroupId(), artifactItem.getArtifactId(), artifactItem.getType(),
                        artifactSet, vr, classifier);
            } catch (MojoExecutionException e) {
                if (culture == null)
                    throw e;
                hasSomething = false;
            }

            if (culture != null) {
                try {
                    String classifier = arch + "-" + getPrimaryCulture(culture);
                    getArtifact(artifactItem.getGroupId(), artifactItem.getArtifactId(), artifactItem.getType(),
                            artifactSet, vr, classifier);
                } catch (MojoExecutionException e) {
                    if (hasSomething == false)
                        throw e;
                }
            }
        }

        // list out all the dependencies with their classifiers
        //      if ("msi".equalsIgnoreCase(artifactItem.getType()) ) {
        //         boolean hasSomething = true;
        //         // even if this module has culture it's base modules may be neutral
        //         try {
        //            String classifier = arch + "-" + "neutral";
        //            getArtifact(artifactItem.getGroupId(), artifactItem.getArtifactId(), artifactItem.getType(), artifactSet, vr, classifier);
        //         } catch (MojoExecutionException e) {
        //            if (culture == null)
        //               throw e;
        //            hasSomething = false;
        //         }
        //
        //         if (culture != null) {
        //            try {
        //               String classifier = arch + "-" + getPrimaryCulture(culture);
        //               getArtifact(artifactItem.getGroupId(), artifactItem.getArtifactId(), artifactItem.getType(), artifactSet, vr, classifier);
        //            } catch (MojoExecutionException e) {
        //               if (hasSomething == false)
        //                  throw e;
        //            }
        //         }
        //      }      
        //      
        // else if ("nar".equalsIgnoreCase(artifactItem.getType())) {
        // get one of both 32 & 64 bit... how do we tell whats there to use?
        // go through nar
        // for (String arch : getPlatforms()) {
        // for (String culture : cultures) {
        // String culture = null;
        // String classifier = arch + "-" + (culture == null ? "neutral" : culture);

        // getArtifact(artifactItem.getGroupId(), artifactItem.getArtifactId(), artifactItem.getType(), artifactSet, vr, "x86-Windows-msvc-shared");
        // }
        // }
        return artifactSet;
    }
}