Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Gradle properties available to settings scripts #12401

Merged
merged 13 commits into from Mar 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,65 @@
/*
* Copyright 2020 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.integtests.composite

import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.integtests.fixtures.executer.GradleContextualExecuter
import spock.lang.IgnoreIf

@IgnoreIf({ GradleContextualExecuter.isInstant() })
class CompositeBuildPropertiesIntegrationTest extends AbstractIntegrationSpec {

def "included build properties take precedence over root build properties"() {
given:
def createBuild = { String buildName, String dir ->
createDir(dir) {
file("gradle.properties") << """
theProperty=$buildName
"""
file("settings.gradle") << """
println("$buildName settings script: " + theProperty)
"""
file("build.gradle") << """
println("$buildName build script: " + theProperty)
"""
}
}

createBuild "root", "."
settingsFile << """
includeBuild 'included'
"""

createBuild "included", "included"
file("included/settings.gradle") << """
includeBuild '../nested'
"""

createBuild "nested", "nested"

when:
run('help')

then:
outputContains("root settings script: root")
outputContains("root build script: root")
outputContains("included settings script: included")
outputContains("included build script: included")
outputContains("nested settings script: nested")
outputContains("nested build script: nested")
}
}
Expand Up @@ -29,6 +29,11 @@ class DistributionPropertiesLoaderIntegrationTest extends AbstractIntegrationSpe
settingsFile << '''
includeBuild 'includedBuild'
println("system_property_available in settings.gradle: ${System.getProperty('system_property_available', 'false')} ")
try {
println("project_property_available in settings.gradle: ${project_property_available} ")
} catch (MissingPropertyException e) {
println("project_property_available in settings.gradle: false ")
}
'''
buildFile << '''
println("system_property_available in root: ${System.getProperty('system_property_available', 'false')} ")
Expand All @@ -45,6 +50,11 @@ class DistributionPropertiesLoaderIntegrationTest extends AbstractIntegrationSpe
'''
file('includedBuild/settings.gradle') << '''
println("system_property_available in included settings.gradle: ${System.getProperty('system_property_available', 'false')} ")
try {
println("project_property_available in included settings.gradle:${project_property_available} ")
} catch (MissingPropertyException e) {
println("project_property_available in included settings.gradle:false ")
}
'''
file('includedBuild/buildSrc/build.gradle') << '''
println("system_property_available in included buildSrc: ${System.getProperty('system_property_available', 'false')} ")
Expand All @@ -61,15 +71,17 @@ class DistributionPropertiesLoaderIntegrationTest extends AbstractIntegrationSpe
then:
outputContains('system_property_available in buildSrc: true')
outputContains('system_property_available in buildSrc: true')
outputContains('project_property_available in buildSrc: true')
outputContains('project_property_available in buildSrc: false')
outputContains('system_property_available in included buildSrc: true')
outputContains('project_property_available in included buildSrc: true')
outputContains('project_property_available in included buildSrc: false')
outputContains('system_property_available in included root: true')
outputContains('project_property_available in included root: true')
outputContains('project_property_available in included root: false')
outputContains('system_property_available in root: true')
outputContains('project_property_available in root: true')
outputContains('system_property_available in settings.gradle: true')
outputContains('project_property_available in settings.gradle: true')
outputContains('system_property_available in included settings.gradle: true')
outputContains('project_property_available in included settings.gradle:false')

cleanup:
executer.withArguments("--stop", "--info").run()
Expand Down
@@ -0,0 +1,114 @@
/*
* Copyright 2020 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.initialization;

import org.gradle.api.internal.properties.GradleProperties;

import javax.annotation.Nullable;
import java.io.File;
import java.util.Map;

public class DefaultGradlePropertiesController implements GradlePropertiesController {

private State state = new NotLoaded();
private final GradleProperties sharedGradleProperties = new SharedGradleProperties();
private final IGradlePropertiesLoader propertiesLoader;

public DefaultGradlePropertiesController(IGradlePropertiesLoader propertiesLoader) {
this.propertiesLoader = propertiesLoader;
}

@Override
public GradleProperties getGradleProperties() {
return sharedGradleProperties;
}

@Override
public void loadGradlePropertiesFrom(File settingsDir) {
state = state.loadGradlePropertiesFrom(settingsDir);
}

private class SharedGradleProperties implements GradleProperties {

@Nullable
@Override
public String find(String propertyName) {
return gradleProperties().find(propertyName);
}

@Override
public Map<String, String> mergeProperties(Map<String, String> properties) {
return gradleProperties().mergeProperties(properties);
}

private GradleProperties gradleProperties() {
return state.gradleProperties();
}
}

private interface State {

GradleProperties gradleProperties();

State loadGradlePropertiesFrom(File settingsDir);
}

private class NotLoaded implements State {

@Override
public GradleProperties gradleProperties() {
throw new IllegalStateException("GradleProperties has not been loaded yet.");
}

@Override
public State loadGradlePropertiesFrom(File settingsDir) {
return new Loaded(
propertiesLoader.loadGradleProperties(settingsDir),
settingsDir
);
}
}

private static class Loaded implements State {

private final GradleProperties gradleProperties;
private final File propertiesDir;

public Loaded(GradleProperties gradleProperties, File propertiesDir) {
this.gradleProperties = gradleProperties;
this.propertiesDir = propertiesDir;
}

@Override
public GradleProperties gradleProperties() {
return gradleProperties;
}

@Override
public State loadGradlePropertiesFrom(File settingsDir) {
if (!propertiesDir.equals(settingsDir)) {
throw new IllegalStateException(
String.format(
"GradleProperties has already been loaded from '%s' and cannot be loaded from '%s'.",
propertiesDir, settingsDir
)
);
}
return this;
}
}
}
Expand Up @@ -36,16 +36,25 @@ public class DefaultSettingsLoader implements SettingsLoader {
public static final String BUILD_SRC_PROJECT_PATH = ":" + SettingsInternal.BUILD_SRC;
private SettingsProcessor settingsProcessor;
private final BuildLayoutFactory buildLayoutFactory;
private final GradlePropertiesController gradlePropertiesController;

public DefaultSettingsLoader(SettingsProcessor settingsProcessor, BuildLayoutFactory buildLayoutFactory) {
public DefaultSettingsLoader(
SettingsProcessor settingsProcessor,
BuildLayoutFactory buildLayoutFactory,
GradlePropertiesController gradlePropertiesController
) {
this.settingsProcessor = settingsProcessor;
this.buildLayoutFactory = buildLayoutFactory;
this.gradlePropertiesController = gradlePropertiesController;
}

@Override
public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
StartParameter startParameter = gradle.getStartParameter();

SettingsLocation settingsLocation = buildLayoutFactory.getLayoutFor(new BuildLayoutConfiguration(startParameter));
loadGradlePropertiesFrom(settingsLocation);

SettingsInternal settings = findSettingsAndLoadIfAppropriate(gradle, startParameter, settingsLocation, gradle.getClassLoaderScope());
ProjectSpec spec = ProjectSpecs.forStartParameter(startParameter, settings);
if (useEmptySettings(spec, settings, startParameter)) {
Expand All @@ -56,6 +65,12 @@ public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
return settings;
}

private void loadGradlePropertiesFrom(SettingsLocation settingsLocation) {
gradlePropertiesController.loadGradlePropertiesFrom(
settingsLocation.getSettingsDir()
);
}

private boolean useEmptySettings(ProjectSpec spec, SettingsInternal loadedSettings, StartParameter startParameter) {
// Never use empty settings when the settings were explicitly set
if (startParameter.getSettingsFile() != null) {
Expand Down
Expand Up @@ -32,14 +32,24 @@ public class DefaultSettingsLoaderFactory implements SettingsLoaderFactory {
private final PublicBuildPath publicBuildPath;
private final Instantiator instantiator;
private final BuildLayoutFactory buildLayoutFactory;
private final GradlePropertiesController gradlePropertiesController;

public DefaultSettingsLoaderFactory(SettingsProcessor settingsProcessor, BuildStateRegistry buildRegistry, ProjectStateRegistry projectRegistry, PublicBuildPath publicBuildPath, Instantiator instantiator, BuildLayoutFactory buildLayoutFactory) {
public DefaultSettingsLoaderFactory(
SettingsProcessor settingsProcessor,
BuildStateRegistry buildRegistry,
ProjectStateRegistry projectRegistry,
PublicBuildPath publicBuildPath,
Instantiator instantiator,
BuildLayoutFactory buildLayoutFactory,
GradlePropertiesController gradlePropertiesController
) {
this.settingsProcessor = settingsProcessor;
this.buildRegistry = buildRegistry;
this.projectRegistry = projectRegistry;
this.publicBuildPath = publicBuildPath;
this.instantiator = instantiator;
this.buildLayoutFactory = buildLayoutFactory;
this.gradlePropertiesController = gradlePropertiesController;
}

@Override
Expand Down Expand Up @@ -68,7 +78,12 @@ public SettingsLoader forNestedBuild() {

private SettingsLoader defaultSettingsLoader() {
return new SettingsAttachingSettingsLoader(
new DefaultSettingsLoader(settingsProcessor, buildLayoutFactory),
projectRegistry);
new DefaultSettingsLoader(
settingsProcessor,
buildLayoutFactory,
gradlePropertiesController
),
projectRegistry
);
}
}
@@ -0,0 +1,45 @@
/*
* Copyright 2020 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.initialization;

import org.gradle.api.internal.properties.GradleProperties;

import java.io.File;

/**
* Controls the state (not loaded / loaded) of the attached {@link GradleProperties} instance
* so that the set of Gradle properties is deterministically loaded only once per build.
*/
public interface GradlePropertiesController {

/**
* The {@link GradleProperties} instance attached to this service.
*/
GradleProperties getGradleProperties();

/**
* Loads the immutable set of {@link GradleProperties} from the given directory and
* makes it available to the build.
*
* This method should be called only once per build but multiple calls with the
* same argument are allowed.
*
* @param settingsDir directory where to look for the {@code gradle.properties} file
* @throws IllegalStateException if called with a different argument in the same build
*/
void loadGradlePropertiesFrom(File settingsDir);
}
Expand Up @@ -18,13 +18,13 @@

import org.gradle.StartParameter;
import org.gradle.api.internal.DynamicObjectAware;
import org.gradle.internal.extensibility.ExtensibleDynamicObject;
import org.gradle.api.internal.GradleInternal;
import org.gradle.api.internal.SettingsInternal;
import org.gradle.api.internal.initialization.ClassLoaderScope;
import org.gradle.api.internal.initialization.ScriptHandlerFactory;
import org.gradle.api.internal.initialization.ScriptHandlerInternal;
import org.gradle.groovy.scripts.ScriptSource;
import org.gradle.internal.extensibility.ExtensibleDynamicObject;
import org.gradle.internal.metaobject.DynamicObject;
import org.gradle.internal.reflect.Instantiator;
import org.gradle.internal.service.scopes.ServiceRegistryFactory;
Expand Down