gradle-master/subprojects/ide-native/src/main/java/org/gradle/ide/xcode/tasks/GenerateSchemeFileTask.java
/*
 * Copyright 2017 the original author or authors.
 *
 * 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.
 */

package org.gradle.ide.xcode.tasks;

import org.gradle.api.Action;
import org.gradle.api.Incubating;
import org.gradle.api.tasks.Internal;
import org.gradle.ide.xcode.XcodeProject;
import org.gradle.ide.xcode.internal.DefaultXcodeProject;
import org.gradle.ide.xcode.internal.XcodeTarget;
import org.gradle.ide.xcode.tasks.internal.XcodeSchemeFile;
import org.gradle.plugins.ide.api.XmlGeneratorTask;
import org.gradle.work.DisableCachingByDefault;

import java.io.File;

import static org.gradle.ide.xcode.internal.DefaultXcodeProject.BUILD_DEBUG;
import static org.gradle.ide.xcode.internal.DefaultXcodeProject.TEST_DEBUG;

/**
 * Task for generating a Xcode scheme file (e.g. {@code Foo.xcodeproj/xcshareddata/xcschemes/Foo.xcscheme}). An Xcode scheme defines a collection of targets to build, a configuration to use when building, and a collection of tests to execute.
 *
 * <p>You can have as many schemes as you want, but only one can be active at a time. You can include a scheme in a project—in which case it’s available in every workspace that includes that project, or in the workspace—in which case it’s available only in that workspace.</p>
 *
 * <p>This task is used in conjunction with {@link org.gradle.ide.xcode.tasks.GenerateXcodeProjectFileTask}.</p>
 *
 * @see org.gradle.ide.xcode.XcodeProject
 * @since 4.2
 */
@Incubating
@DisableCachingByDefault(because = "Not made cacheable, yet")
public abstract class GenerateSchemeFileTask extends XmlGeneratorTask<XcodeSchemeFile> {
    public DefaultXcodeProject xcodeProject;

    @Internal
    public XcodeProject getXcodeProject() {
        return xcodeProject;
    }

    public void setXcodeProject(XcodeProject xcodeProject) {
        this.xcodeProject = (DefaultXcodeProject) xcodeProject;
    }

    @Override
    protected void configure(XcodeSchemeFile schemeFile) {
        configureBuildAction(schemeFile.getBuildAction());
        configureTestAction(schemeFile.getTestAction());
        configureLaunchAction(schemeFile.getLaunchAction());
        configureArchiveAction(schemeFile.getArchiveAction());
        configureAnalyzeAction(schemeFile.getAnalyzeAction());
        configureProfileAction(schemeFile.getProfileAction());
    }

    private void configureBuildAction(XcodeSchemeFile.BuildAction action) {
        for (final XcodeTarget xcodeTarget : xcodeProject.getTargets()) {
            action.entry(new Action<XcodeSchemeFile.BuildActionEntry>() {
                @Override
                public void execute(XcodeSchemeFile.BuildActionEntry buildActionEntry) {
                    buildActionEntry.setBuildForAnalysing(!xcodeTarget.isUnitTest());
                    buildActionEntry.setBuildForArchiving(!xcodeTarget.isUnitTest());
                    buildActionEntry.setBuildForProfiling(!xcodeTarget.isUnitTest());
                    buildActionEntry.setBuildForRunning(!xcodeTarget.isUnitTest());
                    buildActionEntry.setBuildForTesting(xcodeTarget.isUnitTest());
                    buildActionEntry.setBuildableReference(toBuildableReference(xcodeTarget));
                }
            });
        }
    }

    private void configureTestAction(XcodeSchemeFile.TestAction action) {
        action.setBuildConfiguration(BUILD_DEBUG);

        for (final XcodeTarget xcodeTarget : xcodeProject.getTargets()) {
            if (xcodeTarget.isUnitTest()) {
                action.setBuildConfiguration(TEST_DEBUG);
                action.entry(new Action<XcodeSchemeFile.TestableEntry>() {
                    @Override
                    public void execute(XcodeSchemeFile.TestableEntry testableEntry) {
                        testableEntry.setSkipped(false);
                        XcodeSchemeFile.BuildableReference buildableReference = toBuildableReference(xcodeTarget);
                        testableEntry.setBuildableReference(buildableReference);
                    }
                });
            }
        }
    }

    private void configureLaunchAction(XcodeSchemeFile.LaunchAction action) {
        action.setBuildConfiguration(xcodeProject.getTargets().iterator().next().getDefaultConfigurationName().get());
        for (XcodeTarget xcodeTarget : xcodeProject.getTargets()) {
            XcodeSchemeFile.BuildableReference buildableReference = toBuildableReference(xcodeTarget);
            if (xcodeTarget.isRunnable()) {
                action.setBuildableProductRunnable(buildableReference);
            }
            action.setBuildableReference(buildableReference);
            break;
        }
    }

    private void configureArchiveAction(XcodeSchemeFile.ArchiveAction action) {
        action.setBuildConfiguration(BUILD_DEBUG);
    }

    private void configureProfileAction(XcodeSchemeFile.ProfileAction action) {
        action.setBuildConfiguration(BUILD_DEBUG);
    }

    private void configureAnalyzeAction(XcodeSchemeFile.AnalyzeAction action) {
        action.setBuildConfiguration(BUILD_DEBUG);
    }

    @Override
    public File getInputFile() {
        return null;
    }

    @Override
    protected XcodeSchemeFile create() {
        return new XcodeSchemeFile(getXmlTransformer());
    }

    private XcodeSchemeFile.BuildableReference toBuildableReference(XcodeTarget target) {
        XcodeSchemeFile.BuildableReference buildableReference = new XcodeSchemeFile.BuildableReference();
        buildableReference.setBuildableIdentifier("primary");
        buildableReference.setBlueprintIdentifier(target.getId());
        buildableReference.setBuildableName(target.getProductName());
        buildableReference.setBlueprintName(target.getName());
        buildableReference.setContainerRelativePath(getProject().getName() + ".xcodeproj");

        return buildableReference;
    }
}
