[GRADLE-1419] Add support for loading arbitrary properties files as per gradle.properties Created: 04/Mar/11  Updated: 06/Feb/17  Resolved: 06/Feb/17

Status: Resolved
Project: Gradle
Affects Version/s: 1.0-milestone-1
Fix Version/s: None

Type: New Feature
Reporter: Kevin Stembridge Assignee: Unassigned
Resolution: Won't Fix Votes: 15

Issue Links:
Duplicate
Duplicated by GRADLE-871 Provide a convenience mechanism to cr... Resolved

 Description   

Gradle can currently load properties from a file called gradle.properties in the project directory. It would be great if the functionality that loads gradle.properties was made available as part of the dsl in order to load any arbitrary properties file as per the Ant <property> element.



 Comments   
Comment by Hans Dockter [ 04/Mar/11 ]

We definitely want to support this. We plan a lot of additional cool features for configuration management.

Comment by Blaine Simpson [ 26/Apr/11 ]

Since I can't tell which Issue is the primary one among the 2 duplicates, I am duplicating my work-around in both issues:

def props = new Properties();
new File("local.properties").withInputStream

{ props.load(it) }

println "Loaded " + props.size() + " local properties"
props.each(

{ project.setProperty(it.key, it.value)}

)

Comment by Blaine Simpson [ 28/Apr/11 ]

When this is implemented, I'd like some way for values in the properties file to be able to ${reference} other properties set in same file, as well as another arbitrarily specified properties set. This usage is generally supported (with a couple exceptions and constraints) by Spring's PropertyPlaceholders (and newer PropertySources... system), Commons Configuration, and Log4j. For example, the following is useful in a lot of situations:

subSysDirPath=${user.home}/apps/ss1
libDirPath=${subSysDirPath}/lib
srcDirPath=${subSysDirPath}/src

I could do this with a custom class (and have, in a class in HyperSQL), but I don't want to carry around a custom class or groovy file for all of my Gradle projects. I know of no way to do this that is simple and efficient (like the work-around for original issue in my earlier post).

Comment by Peter Niederwieser [ 28/Apr/11 ]

The natural way for Gradle to support "enhanced" configuration files would be via Groovy's ConfigSlurper: http://groovy.codehaus.org/ConfigSlurper
Of course you can use ConfigSlurper directly, just as you already do with the Properties class.

Comment by Blaine Simpson [ 29/Apr/11 ]

But if I comprehend the API of ConfigSlurper correctly, the "natural way" that you recommend doesn't support the basic use cases that I have requested and which Java developers are used to, like the sample properties file contents that I listed above.

I am no expert with Groovy config objects, so if I am getting this wrong, please correct me. To concentrate on one widely-appreciated particular, it is clearly useful to be able to use Java system properties in Java properties files. I use ${java.home}, ${path.separator}, etc. constantly in this way. Not only does ConfigSlurper not support the expansion, but the only expansion method I am aware of (expand) doesn't even support property names with dots in them (creating nested objects which is totally the wrong way to treat System properties). I could use inline "blah${project.getProperty('named.prop')}blah", but this is clean behavior for neither Gradle nor Java. If I am wrong, and there is some way that I can have Gradle use a typical Java properties file like that in my 28/Apr post, just like Commons Configuration, Log4j, Hibernate,... would, please inform.

Comment by Blaine Simpson [ 29/Apr/11 ]

As it looks like Gradle won't be doing it, I bit the bullet and wrote my own methods to support nested property definitions and dotted property names when doing expansions. Current implementation is hard-coded to support nested definitions via system properties, project properties, or other properties set in 'this' property file (including recursively). At some point I should generalize to take an arbitrary list of other maps/properties for nesting.

PART 1, properties-load method:

void loadJavaProps(String filePath, boolean strict) {
    def props = new Properties();
    new File(filePath).withInputStream { props.load(it) }
    println 'Loaded ' + props.size() + ' from ' + filePath
    String newVal;
    boolean haveNewVal;
    int prevCount = props.size();
    def unresolveds = []
    while (prevCount > 0) {
        unresolveds.clear()
        new HashMap(props).each() { pk, pv ->
            haveNewVal = true;
            newVal = pv.replaceAll(/\$\{([^}]+)\}/, {
                if (project.hasProperty(it[1])) return project.property(it[1])
                if (System.properties.containsKey(it[1]))
                    return System.properties[it[1]]
                unresolveds << it[1]
                haveNewVal = false;
                        return it[0]
            })
            if (haveNewVal) {
                project.setProperty(pk, newVal)
                props.remove(pk)
            }
        }
        if (prevCount == props.size()) {
            if (strict)
                throw new GradleException(
                    'Unable to resolve top-level properties: ' + props.keySet()
                    + '\ndue to unresolved references to: ' + unresolveds)
            logger.error(
                    'Unable to resolve top-level properties: ' + props.keySet()
                    + '\ndue to unresolved references to: ' + unresolveds)
            return
        }
        prevCount = props.size()
    }
}

PART 2. Instead of ContentFilterable.expand method, use the following more Java-traditional expander filter:

filter({
    it.replaceAll(/\$\{([^}]+)\}/, {
        if (project.hasProperty(it[1])) return project.property(it[1])
        if (System.properties.containsKey(it[1]))
            return System.properties[it[1]]
        return it[0]
    })
})
Comment by Blaine Simpson [ 16/Nov/11 ]

I made a proper plugin that does everything discussed here.

JavaPropFile Plugin

Comment by Mauro Molinari [ 27/Jul/12 ]

I would also like to see proper Gradle support for other property files. Honestly, it's quite surprising there's no such support out-of-the-box.

With the optimum Blaine's plugin I can just do:
propFileLoader.load(file('product.properties'))

to add properties from a file to the extra properties of my project. This is exactly the minimum I would expect from Gradle.

BTW: Blaine, the latest version of your plugin I found on Maven Central is 0.5.1 and is throwing deprecation warnings because I think it's adding properties dynamically to the project without using the ext namespace.

Comment by Ido [ 11/Oct/12 ]

Initially I used Blaine's plugin, but it uses a long-deprecated API (dynamic properties) and I got concerned that pretty soon I'd have to choose between the plugin and a Gradle upgrade (when Gradle finally removes the deprecated API in a future release).

I then found a solution so simple that it makes using a plugin seem like overkill to begin with. Just slap this method into your project file:

def loadProperties(String sourceFileName) {
    def config = new Properties()
    def propFile = new File(sourceFileName)
    if (propFile.canRead()) {
        config.load(new FileInputStream(propFile))
        for (Map.Entry property in config) {
            ext[property.key] = property.value;
        }
    }
}

Then, use it as follows:

loadProperties 'build.properties'

If the file with the given name doesn't exist, it fails silently.

The properties are loaded as extra properties of your project, so when reading them you don't use the "ext." qualifier (read them just like dynamic properties), as opposed to when writing into them.

Comment by Kevin Stembridge [ 11/Oct/12 ]

Very nice Ido.

I'd love to see the Gradle team make this available as a top-level method. They should also consider making it more flexible in how we specify the files to be loaded. e.g. classpath-relative, collections of files, etc.

Comment by Jonas [ 20/Dec/13 ]

You say "The properties are loaded as extra properties of your project, so when reading them you don't use the "ext." qualifier (read them just like dynamic properties), as opposed to when writing into them."

Could you please give an exact example?

Comment by Blaine Simpson [ 20/Dec/13 ]

I forgot to update this discussion last year. I updated my JavaPropFile plugin promptly. It supports the current API without warnings, and hasn't needed any modification since because of the rigorous unit tests. It will be updated aggresively whenever there is a need because it is used by very active open source projects. H

Ido's snippet satisfies the most common use case. I often need more, like support for types other than strings, nesting of values, ability to reference sys properties (and set them), adding to arbitrary scope (not just the project), validation, default values, etc. See the feature list.

Comment by Ido [ 20/Dec/13 ]

You say "The properties are loaded as extra properties of your project, so when reading them you don't use the "ext." qualifier (read them just like dynamic properties), as opposed to when writing into them."

Could you please give an exact example?

Sure:

myprops.properties
db-host-addr=100.100.100.100
db-port=222
db-name=myDB
build.gradle
...
loadProperties 'myprops.properties'
...
println db-name // output: myDB
...
Comment by Raja Nagendra Kumar [ 11/Jul/14 ]

It would be nice to have this as default method.
Else, we need to include the script for loadProperties everywhere.. as loading properties from any where is so commonly used. More so this method is no optimal as one needs to read and assign to global properties.

Please consider to get this ASAP.

Regards,
Raja Nagendra Kumar

Comment by Benjamin Muschko [ 15/Nov/16 ]

As announced on the Gradle blog we are planning to completely migrate issues from JIRA to GitHub.

We intend to prioritize issues that are actionable and impactful while working more closely with the community. Many of our JIRA issues are inactionable or irrelevant. We would like to request your help to ensure we can appropriately prioritize JIRA issues you’ve contributed to.

Please confirm that you still advocate for your JIRA issue before December 10th, 2016 by:

  • Checking that your issues contain requisite context, impact, behaviors, and examples as described in our published guidelines.
  • Leave a comment on the JIRA issue or open a new GitHub issue confirming that the above is complete.

We look forward to collaborating with you more closely on GitHub. Thank you for your contribution to Gradle!

Comment by Mauro Molinari [ 15/Nov/16 ]

I'm still missing this feature and relying on third-party plugins. I think it should be a core feature.

Comment by Benjamin Muschko [ 06/Feb/17 ]

What I read from the issue are two different aspects:

1. I want to define properties in a different file than gradle.properties.
2. I want to be able to use properties as placeholders within a properties file to form other properties.

Some suggestions here:

1. I'd assume that the main purpose is to define extra properties. Why not just define those properties in a script plugin? Then you don't have to jump through the hoops of translating key/value pairs into extra properties. For example:

myproperties.gradle:

ext {
    myProp = 'test'
}

build.gradle:

apply from: 'myproperties.gradle'

2. You can easily replace portions of an extra property with another property value e.g.

myproperties.gradle:

ext {
    myProp = 'a'
    myFile = "${myProp}.txt"
}

Given these suggestions and other recommendation mentioned in this card, I'd like to just close the issue. From my perspective it is more idiomatic to create the extra properties directly in a Gradle build script than in a properties file. Please open an issue on GitHub (separately by the aspects mentioned above) if you would still like to pursue other approaches.

Generated at Wed Jun 30 11:55:04 CDT 2021 using Jira 8.4.2#804003-sha1:d21414fc212e3af190e92c2d2ac41299b89402cf.