I want to read command line arguments in settings.gradle so that i can add only those submodules in include what i am passing command line.
Can we read command line arguments in settings.gradle?
You can't read whole command line arguments in settings gradle file, though what you can do is to read project properties in settings file and those can be passed with the command line arguments.
For example if you want to specify to include sub-project-1 in your Gradle build you would have to provide this value in a project property like following:
gradlew build -Pincluded.projects=sub-project-1
Note CLI command with option -P defines project property. It has to have specified key and value. In this case key is included.projects and value is sub-project-1.
In settings file you can read it with following getProperties() method on Project object. getProperties().get(String key).
Following is the settings script if you have sub modules with names:
sub-module-1
sub-module-2
sub-module-3
It will read property that includes list of modules to include in build script. If property is empty then all modules will be included, otherwise it selects passed in sub projects names and includes only present ones. There is no validation on sub project names.
// Define all the sub projects
def subprojects = ['sub-project-1', 'sub-project-2', 'sub-project-3'] as Set
// Read all subprojects from the project properties.
// Example of passed in project properties with Gradle CLI with the -P option
// `gradlew build -Pincluded.projects=sub-project-1,sub-project-3`
def includedProjectsKey="included.projects"
def projectsToIncludeInput = hasProperty(includedProjectsKey) ? getProperties().get(includedProjectsKey) : ""
Set<String> projectsToInclude = []
if(projectsToIncludeInput != "") {
// Include passed in sub projects from project arguments
projectsToIncludeInput.toString().split(",").each {
projectsToInclude.add(it)
}
} else {
// Include all sub projects if none is specified
projectsToInclude = subprojects
}
// Include sub projects
projectsToInclude.each {
include it
}
Related
Let's say there is a Gradle task that produces an artifact. For example, a Zip task:
tasks.register("myZip", Zip) {
...
}
Would the following task of type Copy automatically gain a dependency on task myZip?
task copyMyZips(type: Copy) {
from { subprojects.findAll { it.tasks.findByName('myZip') }.myZip }
into '/tmp'
}
Note the really convoluted way of referring to the task myZip.
Yes. The Copy tasks do gain dependency on task outputs and tasks which are mentioned in their from blocks.
Per documentation of method from in class Copy as of Gradle 7.4:
AbstractCopyTask from(Object sourcePath, Closure c)
Specifies the source files or directories for a copy and creates a child CopySourceSpec. The given source path is evaluated as per Project.files(java.lang.Object[]).
in turn, documentation of Project.files(java.lang.Object[]) (emphasis mine):
Returns a ConfigurableFileCollection containing the given files. You can pass any of the following types to this method:
[...most of the list snipped...]
A Task. Converted to the task's output files. The task is executed if the file collection is used as an input to another task.
A TaskOutputs. Converted to the output files the related task. The task is executed if the file collection is used as an input to another task.
Unfortunately, the documentation of Copy does not refer to what is passed into method from as "inputs". However, judging by the code of AbstractCopyTask.java every change to the CopySpec of the Copy task is propagated to the inputs via a ChildSpecListener. The listener is added to the field CopySpecInternal rootSpec .
I need to write a simple task to create a zip from the source code. I need to include a dir called 1-dir or 2-dir depending on a system property. But the name of the directory in the resulting zip should always be dir. So basically, I want to include a dir in a zip (conditionally) and rename it.
I tried the rename method but that does not work.
Any pointers?
It will be:
task zipDir(type: Zip) {
def fromDir = project.hasProperty('from') ? project.from : 'dir1'
from(fromDir)
into('dir')
}
It can be run in the following way gradle zipDir -Pfrom=dir2. If no from property is passed dir1 will be zipped.
If you need system property instead of gradle property, pass -Dfrom=dir2 and use System.properties['from'] instead of project.from.
As part of building a number of projects, I would like to sort the contents in some property-files that are semi-generated but also checked in to source control. The generation/update step in Gradle leaves them in different order (Done in 3rd party plugin code, probably the changing ordering is due to using the Java Properties class internally).
What is the simplest way to sort the contents of a file in Gradle?
The files are not large, reading in the file into lines, sorting and writing out again to the same file should suffice?
The following piece of code should do the job:
new File('lol').with { it.text = it.readLines().findAll { it }.sort().join('\n') }
With gradle task it will be:
task sortLines << {
new File('lol').with { it.text = it.readLines().findAll { it }.sort().join('\n') }
}
As part of a deploy task in Gradle, I want to change the value of a property in foo.properties to point to a production database instead of a development database.
I'd rather not replace the whole file outright, as it's rather large and it means we would have to maintain two separate versions that only differ on a single line.
What is the best way to accomplish this?
You can use the ant.propertyfile task:
ant.propertyfile(
file: "myfile.properties") {
entry( key: "propertyName", value: "propertyValue")
entry( key: "anotherProperty", operation: "del")
}
You should be able to fire off an ant "replace" task that does what you want: http://ant.apache.org/manual/Tasks/replace.html
ant.replace(file: "blah", token: "wibble", value: "flibble")
Create a properties object, then create the file object with the targeted properties file path, load the file on the properties object with load, set the desired property with setProperty, and save the changes with store.
def var = new Properties()
File myfile = file("foo.properties");
var.load(myfile.newDataInputStream())
var.setProperty("db", "prod")
var.store(myfile.newWriter(), null)
A simple solution is to code a task that uses java.util.Properties to write the file. If you really want to incrementally update the file, you'll have to implement this on your own. Or maybe you find an Ant task that does what you want (all Ant tasks can be used as-is from Gradle). For best results, you should also declare the inputs and outputs of the task, so that Gradle only executes the tasks when the properties file needs to be changed.
You can use ReplaceTokens
Say you have a file called db.properties in src/main/java/resources/com.stackoverlow (the location doesn't matter) with the following content
database.url=#url#
Note that the # surrounding the url text is required.
You can then define this in your build.gradle file.
processResources {
filter ReplaceTokens, tokens: [
"url": "https://stackoverflow.com"
]
}
When you build your code, this would replace #url# with https://stackoverflow.com.
If you are only interested in applying this rule to a specific file, you can add a filesMatching
processResources {
filesMatching('**/db.properties') {
filter ReplaceTokens, tokens: [
"url": "https://stackoverflow.com"
]
}
}
See gradle doc for more explanation
I need to make sure a custom build tool that operates on a lot of files is always run when one of those files are changed.
I know you can specify "Additional Dependencies" for the custom build tool, but is there a better way than specifying one file per line?
Just made the build tool depend on one file. Created another custom build tool that runs before it and that touches this file if any of the real dependencies have changed. The advantage is that I now have quite a lot of flexibility and don't need to change any of the project settings if the dependencies change - that's taken care of via a database that the new custom build tool accesses.
"Additional Dependencies" is the correct and only documented way. You could adjust the contents of this field for the build tool in the Project file using an external tool to save on the troubles of doing a copy & paste & typo-adjust.
It won't be quite as reliable, but you could put all files in a subfolder and just make the folder a dependency.
A little bit hack:
(1) group the dependency files into a separate folder
(2) create a jscript file detect.js as follow:
var output = WScript.arguments(0);
var folder = WScript.arguments(1);
var fso = new ActiveXObject("Scripting.FileSystemObject");
var objOutput = fso.GetFile(output);
var objFolder = fso.GetFolder(folder);
// if the output file is older than input folder,
// delete output file to force regenerate
if (objOutput.DateLastModified < objFolder.DateLastModified) {
fso.DeleteFile(objOutput);
} else {
// if the output file is older than one of files in the input folder,
// delete output file to force regenerate
var e = new Enumerator(objFolder.Files);
for (; !e.atEnd(); e.moveNext()) {
if (objOutput.DateLastModified < e.item().DateLastModified)
fso.DeleteFile(objOutput);
break;
}
}
}
(2) Add command lines to Pre-Build Event as follow:
cscript.exe /nologo detect.js $(Output) $(InputFolder)
(3) Setup the Custom Buld Step to force the Pre-Build Event to occur, i.e.
Command Line: echo --------------
Outputs: echo.fake
Execute After: PreBuildEvent