├── .editorconfig
├── .gitignore
├── .idea
├── icon.png
├── inspectionProfiles
│ └── Project_Default.xml
├── kotlinc.xml
├── ktlint-plugin.xml
├── ktlint.xml
├── markdown.xml
└── vcs.xml
├── LICENSE
├── README-zh-CN.md
├── README.md
├── build.gradle.kts
├── docs
├── changelog-zh-CN.md
├── changelog.md
├── guide-zh-CN.md
└── guide.md
├── gradle.properties
├── gradle
├── sweet-dependency
│ └── sweet-dependency-config.yaml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── img-src
└── icon.png
├── settings.gradle.kts
└── sweetproperty-gradle-plugin
├── build.gradle.kts
└── src
└── main
└── java
├── com
└── highcapable
│ └── sweetproperty
│ ├── SweetProperty.kt
│ ├── gradle
│ ├── entity
│ │ └── ProjectDescriptor.kt
│ ├── factory
│ │ ├── ExtensionAwareFactory.kt
│ │ └── GradleProjectFactory.kt
│ └── proxy
│ │ └── IGradleLifecycle.kt
│ ├── plugin
│ ├── SweetPropertyExtension.kt
│ ├── SweetPropertyPlugin.kt
│ ├── config
│ │ ├── default
│ │ │ └── DefaultConfigs.kt
│ │ ├── factory
│ │ │ └── SweetPropertyConfigsFactory.kt
│ │ ├── proxy
│ │ │ └── ISweetPropertyConfigs.kt
│ │ └── type
│ │ │ └── GenerateLocationType.kt
│ ├── extension
│ │ ├── accessors
│ │ │ └── proxy
│ │ │ │ └── IExtensionAccessors.kt
│ │ └── dsl
│ │ │ └── configure
│ │ │ └── SweetPropertyConfigureExtension.kt
│ ├── generator
│ │ ├── PropertiesAccessorsGenerator.kt
│ │ ├── PropertiesSourcesGenerator.kt
│ │ └── factory
│ │ │ └── GeneratorFactory.kt
│ └── helper
│ │ ├── PluginUpdateHelper.kt
│ │ └── PropertiesDeployHelper.kt
│ └── utils
│ ├── FileFactory.kt
│ ├── HttpFactory.kt
│ ├── VariableFactory.kt
│ ├── code
│ ├── CodeCompiler.kt
│ ├── entity
│ │ └── MavenPomData.kt
│ └── factory
│ │ └── CodeCompilerFactory.kt
│ └── debug
│ ├── SError.kt
│ └── SLog.kt
└── org
└── gradle
└── kotlin
└── dsl
└── SweetPropertySettingsExtensionFactory.kt
/.editorconfig:
--------------------------------------------------------------------------------
1 | # noinspection EditorConfigKeyCorrectness
2 | [{*.kt,*.kts}]
3 | ktlint_standard_annotation = disabled
4 | ktlint_standard_filename = disabled
5 | ktlint_standard_wrapping = disabled
6 | ktlint_standard_import-ordering = enabled
7 | ktlint_standard_max-line-length = disabled
8 | ktlint_standard_multiline-if-else = disabled
9 | ktlint_standard_argument-list-wrapping = disabled
10 | ktlint_standard_parameter-list-wrapping = disabled
11 | ktlint_standard_trailing-comma-on-declaration-site = disabled
12 | ktlint_function_signature_body_expression_wrapping = multiline
13 | ktlint_standard_string-template-indent = disabled
14 | ktlint_standard_function-signature = disabled
15 | ktlint_standard_trailing-comma-on-call-site = disabled
16 | ktlint_standard_multiline-expression-wrapping = disabled
17 | ktlint_standard_no-empty-first-line-in-class-body = disabled
18 | ktlint_standard_if-else-wrapping = disabled
19 | ktlint_standard_if-else-bracing = disabled
20 | ktlint_standard_statement-wrapping = disabled
21 | ktlint_standard_blank-line-before-declaration = disabled
22 | ktlint_standard_no-empty-file = disabled
23 | ktlint_standard_property-naming = disabled
24 | ktlint_standard_function-naming = disabled
25 | ktlint_standard_chain-method-continuation = disabled
26 | ktlint_standard_class-signature = disabled
27 | ktlint_standard_condition-wrapping = disabled
28 | ktlint_standard_class-signature = disabled
29 | ktlint_standard_no-trailing-spaces = disabled
30 | ktlint_standard_multiline-loop = disabled
31 | ij_continuation_indent_size = 2
32 | indent_size = 4
33 | indent_style = space
34 | insert_final_newline = false
35 | max_line_length = 150
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Fully .gtignore for IntelliJ, Android Studio and Gradle based Java projects
2 | ## References:
3 | ## - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
4 | ## - https://github.com/android/platform-samples/blob/main/.gitignore
5 |
6 | # User-specific stuff
7 | .idea/**/workspace.xml
8 | .idea/**/tasks.xml
9 | .idea/**/usage.statistics.xml
10 | .idea/**/dictionaries
11 | .idea/**/shelf
12 |
13 | # AWS User-specific
14 | .idea/**/aws.xml
15 |
16 | # Generated files
17 | .idea/**/contentModel.xml
18 |
19 | # Sensitive or high-churn files
20 | .idea/**/dataSources/
21 | .idea/**/dataSources.ids
22 | .idea/**/dataSources.local.xml
23 | .idea/**/sqlDataSources.xml
24 | .idea/**/dynamic.xml
25 | .idea/**/uiDesigner.xml
26 | .idea/**/dbnavigator.xml
27 |
28 | # Gradle
29 | .idea/**/gradle.xml
30 | .idea/**/libraries
31 |
32 | # Gradle and Maven with auto-import
33 | .idea/.name
34 | .idea/artifacts
35 | .idea/compiler.xml
36 | .idea/jarRepositories.xml
37 | .idea/modules.xml
38 | .idea/*.iml
39 | .idea/modules
40 | .idea/caches
41 | .idea/material_theme**
42 | .idea/other.xml
43 | *.iml
44 | *.ipr
45 |
46 | # Kotlin
47 | .kotlin
48 |
49 | # Misc
50 | .idea/misc.xml
51 |
52 | # CMake
53 | cmake-build-*/
54 |
55 | # Mongo Explorer plugin
56 | .idea/**/mongoSettings.xml
57 |
58 | # File-based project format
59 | *.iws
60 |
61 | # IntelliJ
62 | out/
63 |
64 | # mpeltonen/sbt-idea plugin
65 | .idea_modules/
66 |
67 | # JIRA plugin
68 | atlassian-ide-plugin.xml
69 |
70 | # Cursive Clojure plugin
71 | .idea/replstate.xml
72 |
73 | # SonarLint plugin
74 | .idea/sonarlint/
75 |
76 | # Crashlytics plugin (for Android Studio and IntelliJ)
77 | com_crashlytics_export_strings.xml
78 | crashlytics.properties
79 | crashlytics-build.properties
80 | fabric.properties
81 |
82 | # Editor-based Rest Client
83 | .idea/httpRequests
84 |
85 | # Android studio 3.1+ serialized cache file
86 | .idea/caches/build_file_checksums.ser
87 |
88 | # Android studio 3.1+ additional
89 | .idea/deployment*.xml
90 | .idea/assetWizardSettings.xml
91 | .idea/androidTestResultsUserPreferences.xml
92 |
93 | # Android projects
94 | **/local.properties
95 | /captures
96 | .externalNativeBuild
97 | .cxx
98 |
99 | # Gradle projects
100 | .gradle
101 | build/
102 |
103 | # Mkdocs temporary serving folder
104 | docs-gen
105 | site
106 | *.bak
107 | .idea/appInsightsSettings.xml
108 |
109 | # Mac OS
110 | .DS_Store
--------------------------------------------------------------------------------
/.idea/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HighCapable/SweetProperty/9e7d5ec5745b23f1cc3ac1235900131f7a6cc0f8/.idea/icon.png
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/ktlint-plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MANUAL
5 | false
6 |
7 |
--------------------------------------------------------------------------------
/.idea/ktlint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 |
6 |
--------------------------------------------------------------------------------
/.idea/markdown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright HighCapable [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | https://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README-zh-CN.md:
--------------------------------------------------------------------------------
1 | # Sweet Property
2 |
3 | [](https://github.com/HighCapable/SweetProperty/blob/master/LICENSE)
4 | [](https://github.com/HighCapable/SweetProperty/releases)
5 | [](https://t.me/HighCapable_Dev)
6 | [](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf)
7 |
8 |
9 |
10 | 一个轻松在任意地方获取项目属性的 Gradle 插件。
11 |
12 | [English](README.md) | 简体中文
13 |
14 | | | [HighCapable](https://github.com/HighCapable) |
15 | |-------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|
16 |
17 | 这个项目属于上述组织,**点击上方链接关注这个组织**,发现更多好项目。
18 |
19 | ## 这是什么
20 |
21 | 这是一个用来轻松获取 Gradle 项目属性配置文件 `gradle.properties` 中键值的 Gradle 插件。
22 |
23 | 在使用 Kotlin DSL 作为构建脚本后,无法直接使用 Groovy 弱类型语言获取 `gradle.properties` 中键值的功能。
24 |
25 | 这个时候我们只能使用类似 `properties["custom_key"]` 的方式来进行获取,看起来很麻烦,而且键值名称一旦疏忽造成错误,就会引发问题。
26 |
27 | 这就是这个项目诞生的原因,它的作用是根据指定的属性配置文件生成键值实体类,在构建脚本以及项目中畅通无阻地访问你设置的属性。
28 |
29 | ## 兼容性
30 |
31 | 理论支持不是很旧的 Gradle,建议版本为 `7.x.x` 及以上。
32 |
33 | 支持包含 Kotlin 插件的 Java 项目和 Android 项目,其它类型的项目暂不支持。
34 |
35 | > 构建脚本语言
36 |
37 | - Kotlin DSL
38 |
39 | 推荐优先使用此语言作为构建脚本语言,这也是目前 Gradle 推荐的语言。
40 |
41 | - Groovy DSL
42 |
43 | 部分功能可能无法兼容,在后期会逐渐放弃支持,且部分功能会无法使用。
44 |
45 | ## 开始使用
46 |
47 | - [点击这里](docs/guide-zh-CN.md) 查看使用文档
48 |
49 | ## 更新日志
50 |
51 | - [点击这里](docs/changelog-zh-CN.md) 查看历史更新日志
52 |
53 | ## 项目推广
54 |
55 | 如果你正在寻找一个可以自动管理 Gradle 项目依赖的 Gradle 插件,你可以了解一下 [SweetDependency](https://github.com/HighCapable/SweetDependency) 项目。
56 |
57 | 本项目同样使用了 **SweetDependency**。
58 |
59 |
60 |
61 |
嘿,还请君留步!👋
62 |
这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。
63 |
如果下方的项目能为你提供帮助,不妨为我点个 star 吧!
64 |
所有项目免费、开源,遵循对应开源许可协议。
65 |
66 |
67 |
68 | ## Star History
69 |
70 | 
71 |
72 | ## 许可证
73 |
74 | - [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0)
75 |
76 | ```
77 | Apache License Version 2.0
78 |
79 | Copyright (C) 2019 HighCapable
80 |
81 | Licensed under the Apache License, Version 2.0 (the "License");
82 | you may not use this file except in compliance with the License.
83 | You may obtain a copy of the License at
84 |
85 | https://www.apache.org/licenses/LICENSE-2.0
86 |
87 | Unless required by applicable law or agreed to in writing, software
88 | distributed under the License is distributed on an "AS IS" BASIS,
89 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
90 | See the License for the specific language governing permissions and
91 | limitations under the License.
92 | ```
93 |
94 | 版权所有 © 2019 HighCapable
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sweet Property
2 |
3 | [](https://github.com/HighCapable/SweetProperty/blob/master/LICENSE)
4 | [](https://github.com/HighCapable/SweetProperty/releases)
5 | [](https://t.me/HighCapable_Dev)
6 | [](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf)
7 |
8 |
9 |
10 | An easy get project properties anywhere Gradle plugin.
11 |
12 | English | [简体中文](README-zh-CN.md)
13 |
14 | | | [HighCapable](https://github.com/HighCapable) |
15 | |-------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|
16 |
17 | This project belongs to the above-mentioned organization, **click the link above to follow this organization** and discover more good projects.
18 |
19 | ## What's this
20 |
21 | This is a Gradle plugin to easily get the key-values in the Gradle project properties configuration file `gradle.properties`.
22 |
23 | After using Kotlin DSL as a build script, it is not possible to directly use the Groovy weakly typed language to get the key-values functions
24 | in `gradle.properties`.
25 |
26 | At this time, we can only use a method like `properties["custom_key"]` to obtain it, which seems troublesome, and if the key name is negligent
27 | and causes an error, it will cause problems.
28 |
29 | This is the reason why this project was born.
30 |
31 | Its function is to generate key-values entity class according to the specified properties configuration file, and unimpeded access to the properties
32 | you set in the build script and project.
33 |
34 | ## Compatibility
35 |
36 | The theory supports not very old Gradle, the recommended version is `7.x.x` and above.
37 |
38 | Java projects and Android projects containing Kotlin plugins are supported, other types of projects are not supported yet.
39 |
40 | > Build Script Language
41 |
42 | - Kotlin DSL
43 |
44 | It is recommended to use this language as the build script language first, which is also the language currently recommended by Gradle.
45 |
46 | - Groovy DSL
47 |
48 | Some functions may be incompatible, support will be gradually dropped in the future, and some functions may become unavailable.
49 |
50 | ## Get Started
51 |
52 | - [Click here](docs/guide.md) to view the documentation
53 |
54 | ## Changelog
55 |
56 | - [Click here](docs/changelog.md) to view the historical changelog
57 |
58 | ## Promotion
59 |
60 | If you are looking for a Gradle plugin that can automatically manage Gradle project dependencies,
61 | you can check out the [SweetDependency](https://github.com/HighCapable/SweetDependency) project.
62 |
63 | This project also uses **SweetDependency**.
64 |
65 |
66 |
67 |
Hey, please stay! 👋
68 |
Here are related projects such as Android development tools, UI design, Gradle plugins, Xposed Modules and practical software.
69 |
If the project below can help you, please give me a star!
70 |
All projects are free, open source, and follow the corresponding open source license agreement.
71 |
72 |
73 |
74 | ## Star History
75 |
76 | 
77 |
78 | ## License
79 |
80 | - [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0)
81 |
82 | ```
83 | Apache License Version 2.0
84 |
85 | Copyright (C) 2019 HighCapable
86 |
87 | Licensed under the Apache License, Version 2.0 (the "License");
88 | you may not use this file except in compliance with the License.
89 | You may obtain a copy of the License at
90 |
91 | https://www.apache.org/licenses/LICENSE-2.0
92 |
93 | Unless required by applicable law or agreed to in writing, software
94 | distributed under the License is distributed on an "AS IS" BASIS,
95 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
96 | See the License for the specific language governing permissions and
97 | limitations under the License.
98 | ```
99 |
100 | Copyright © 2019 HighCapable
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | autowire(libs.plugins.kotlin.jvm) apply false
3 | autowire(libs.plugins.maven.publish) apply false
4 | }
--------------------------------------------------------------------------------
/docs/changelog-zh-CN.md:
--------------------------------------------------------------------------------
1 | # 更新日志
2 |
3 | ## 1.0.0 | 2023.09.03
4 |
5 | - 首个版本提交至 Maven
6 |
7 | ## 1.0.1 | 2023.09.04
8 |
9 | - 新增启用 `isEnableTypeAutoConversion` 后可以使用 '' 或 "" 强制设置一个键值内容为字符串类型
10 | - 修改生成的代码中属性键值的内容使用优化后的类型呈现
11 |
12 | ## 1.0.2 | 2023.09.07
13 |
14 | - 使用 `net.lingala.zip4j` 取代 JDK 默认创建压缩文档功能修复在 Windows 平台中 Gradle 8.0.2+ 版本创建的 JAR 损坏导致找不到生成的 Class 问题
15 | - 重构自动生成代码部分的装载功能,增加可能找不到 Class 的错误提示
16 | - 作废了 ~~`propertiesFileName`~~ 方法
17 | - 新增 `propertiesFileNames` 方法,现在你可以同时设置一组属性配置文件名称了
18 | - 新增 `includeKeys` 方法,现在你可以设置仅包含的属性键值名称数组了
19 | - 新增 `keyValuesRules` 方法,现在你可以在属性键值装载过程中修改键值内容的实际解析结果
20 |
21 | ## 1.0.3 | 2023.09.26
22 |
23 | - 自动生成代码功能将始终输出源码文件,以方便在生成失败的时候进行调试
24 | - 修复 Gradle 生命周期问题
25 | - 修复根项目大小写变化后识别为两个项目的问题
26 | - 修复在全局配置中使用过其它配置方法后,子项目的 `all` 方法失效问题
27 | - 改进并采用 Gradle 项目命名规范
28 | - 新增插件自身检查更新功能
29 | - 一些其它功能性的改进
30 |
31 | ## 1.0.4 | 2023.11.04
32 |
33 | - 修复类似 `a=some` 和 `a_b=some` 的属性键值名称会造成重复方法名称的问题
34 | - 修复使用 `${...}` 生成的插值内容依然会携带字符串类型引号问题
35 | - 生成的代码使用 `@Nonnull` 标记以使其能够在 Kotlin DSL 脚本中识别为非空返回值类型
36 | - 新增 `project(...)` 配置方法支持同时配置多个项目
37 | - 一些其它功能性的改进
38 |
39 | ## 1.0.5 | 2023.11.08
40 |
41 | - 修复遇到特殊字符和重复键值名称造成代码生成失败的严重问题
--------------------------------------------------------------------------------
/docs/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.0.0 | 2023.09.03
4 |
5 | - The first version is submitted to Maven
6 |
7 | ## 1.0.1 | 2023.09.04
8 |
9 | - After enabling `isEnableTypeAutoConversion`, you can use '' or "" to force the content of a value to be a string type
10 | - Modify the content of the properties key-values in the generated code to use the optimized type to render
11 |
12 | ## 1.0.2 | 2023.09.07
13 |
14 | - Use `net.lingala.zip4j` to replace JDK's default function of creating compressed files and fix the problem that the JAR created by Gradle 8.0.2+
15 | version on Windows platform is broken and the generated classes cannot be found
16 | - Refactor the loading function of the automatically generated code part, and add an error message that classes may not be found
17 | - Deprecated ~~`propertiesFileName`~~ method
18 | - Added `propertiesFileNames` method, now you can set a group of properties file names at the same time
19 | - Added `includeKeys` method, now you can set an array of properties key names to include only
20 | - Added `keyValuesRules` method, now you can modify the actual parsing result of the value content during the properties key-values loading process
21 |
22 | ## 1.0.3 | 2023.09.26
23 |
24 | - The automatic code generation function will always output source code files to facilitate debugging when the generation fails
25 | - Fix Gradle lifecycle problem
26 | - Fix root project was recognized as two projects after the case was changed
27 | - Fix `all` function of the sub-project fails after using other configuration functions in the global configuration
28 | - Improve and adopt Gradle project naming convention
29 | - Added plugin own update function
30 | - Some other functional improvements
31 |
32 | ## 1.0.4 | 2023.11.04
33 |
34 | - Fix the issue where attribute key value names like `a=some` and `a_b=some` would cause duplicate method names
35 | - Fix the problem that the interpolation content generated using `${...}` still carries string type quotes
36 | - Generated code is marked with `@Nonnull` to make it recognized as a non-null return type in Kotlin DSL scripts
37 | - Added `project(...)` configuration method to support configuring multiple projects at the same time
38 | - Some other functional improvements
39 |
40 | ## 1.0.5 | 2023.11.08
41 |
42 | - Fix a serious issue that caused code generation failure when encountering special characters and duplicate key-value names
--------------------------------------------------------------------------------
/docs/guide-zh-CN.md:
--------------------------------------------------------------------------------
1 | # Sweet Property 使用文档
2 |
3 | 在开始使用之前,建议你仔细阅读此文档,以便你能更好地了解它的作用方式与功能。
4 |
5 | 如果你的项目依然在使用 Groovy DSL 进行管理,推荐迁移到 Kotlin DSL。
6 |
7 | 在 Groovy DSL 中使用此插件发生的任何问题,我们都将不再负责排查和修复,并且在后期版本可能会完全不再支持 Groovy DSL。
8 |
9 | 注意:此文档中将不再详细介绍在 Groovy DSL 中的使用方法。
10 |
11 | ## 前提条件
12 |
13 | 请注意 `SweetProperty` 推荐使用 `pluginManagement` 新方式进行装载,它是自 Gradle `7.x.x` 版本开始添加的功能。
14 |
15 | 如果你的项目依然在使用 `buildscript` 的方式进行管理,推荐迁移到新方式,这里将不再提供旧版本的使用方式说明。
16 |
17 | ## 快速开始
18 |
19 | 首先,打开你根项目的 `settings.gradle.kts`。
20 |
21 | 在你根项目的 `settings.gradle.kts` 中加入如下代码。
22 |
23 | 如果已经存在 `pluginManagement` 则不需要重复添加。
24 |
25 | > 示例如下
26 |
27 | ```kotlin
28 | pluginManagement {
29 | repositories {
30 | gradlePluginPortal()
31 | google()
32 | mavenCentral()
33 | }
34 | }
35 | plugins {
36 | id("com.highcapable.sweetproperty") version ""
37 | }
38 | ```
39 |
40 | 请将上述代码中的 `` 替换为
41 | [Release](https://github.com/HighCapable/SweetProperty/releases) 中的最新版本, 请注意**不要** 在后方加入 `apply false`。
42 |
43 | 上述配置完成后,运行一次 Gradle Sync。
44 |
45 | 此时 `SweetProperty` 将会自动搜索根项目和每个子项目中的 `gradle.properties` 文件,并读取其中的属性键值,为每个项目生成对应的代码。
46 |
47 | ## 功能配置
48 |
49 | 你可以对 `SweetProperty` 进行配置来实现自定义和个性化功能。
50 |
51 | `SweetProperty` 为你提供了相对丰富的可自定义功能,下面是这些功能的说明与配置方法。
52 |
53 | 请在你的 `settings.gradle.kts` 中添加 `sweetProperty` 方法块以开始配置 `SweetProperty`。
54 |
55 | > 示例如下
56 |
57 | ```kotlin
58 | sweetProperty {
59 | // 启用 SweetProperty,设置为 false 将禁用所有功能
60 | isEnable = true
61 | // 全局配置
62 | // 你可以在全局配置中修改所有项目中的配置
63 | // 每个项目中未进行声明的配置将使用全局配置
64 | // 每个配置方法块中的功能完全一致
65 | // 你可以参考下方根项目、子项目的配置方法
66 | global {
67 | // 通用代码生成功能
68 | // 在这里你可以同时配置构建脚本和项目生成代码的相关功能
69 | all {
70 | // 启用功能
71 | // 你可以分别对 "sourcesCode"、"buildScript" 进行设置
72 | isEnable = true
73 | // 是否启用排除非字符串类型键值内容
74 | // 默认启用,启用后将从属性键值中排除不是字符串类型的键值及内容
75 | // 这可以排除例如一些系统环境变量的配置或内存中的数据
76 | isEnableExcludeNonStringValue = true
77 | // 是否启用类型自动转换功能
78 | // 默认启用,启用后将自动识别属性键值中的类型并转换为对应的类型
79 | // 在启用后如果你想要强制设置一个键值内容为字符串类型,你可以使用单引号或双引号包裹整个字符串
80 | // 注意:在关闭此功能后如上所述的功能也将同时失效
81 | // 例如 name=hello 和 number=1 它们将会被自动转换为 String 和 Int
82 | // 例如 stringNumber="1" 或 stringNumber='1' 它们将会被强制转换为 String
83 | isEnableTypeAutoConversion = true
84 | // 是否启用键值内容插值功能
85 | // 默认启用,启用后将自动识别属性键值内容中的 ${...} 内容并进行替换
86 | // 注意:插值的内容仅会从当前 (当前配置文件) 属性键值列表进行查找
87 | isEnableValueInterpolation = true
88 | // 设置属性配置文件名称数组
89 | // 属性配置文件将根据你设置的文件名称自动从当前根项目、子项目以及用户目录的根目录进行获取
90 | // 你可以添加多组属性配置文件名称,将按照顺序依次进行读取
91 | // 一般情况下不需要修改此设置,错误的文件名将导致获取到空键值内容
92 | // 你可以配置 "isAddDefault" 参数来决定是否添加默认的 "gradle.properties" 文件名称
93 | // 如果你有一个或多个自定义名称的属性键值文件,你可以修改这里的设置
94 | // 注意:建议为每个项目单独配置,而不是在全局中修改,以防发生问题
95 | propertiesFileNames(
96 | "some_other_1.properties",
97 | "some_other_2.properties",
98 | isAddDefault = true
99 | )
100 | // 设置固定存在的属性键值数组
101 | // 在这里可以设置一些一定存在的键值,这些键值无论能否从属性键值中得到都会进行生成
102 | // 这些键值在属性键值存在时使用属性键值的内容,不存在时使用这里设置的内容
103 | // 注意:属性键值名称不能存在特殊符号以及空格,否则可能会生成失败
104 | // 例如:你需要获取存于自己本机的密钥或证书,但是其他人的设备上没有这些键值
105 | // 此时这个功能就会变得非常有用,你可以设置在没有这些键值时的默认值
106 | // 当然,你也可以使用这个功能强行向生成的代码中添加额外的属性键值
107 | permanentKeyValues(
108 | "permanent.some.key1" to "some_value_1",
109 | "permanent.some.key2" to "some_value_2"
110 | )
111 | // 设置需要排除的属性键值名称数组
112 | // 在这里可以设置一些你希望从已知的属性键值中排除的键值名称
113 | // 这些键值在属性键值存在它们时被排除,不会出现在生成的代码中
114 | // 注意:如果你排除了 "permanentKeyValues" 中设置的键值,
115 | // 那么它们只会变为你设置的初始键值内容并继续保持存在
116 | // 你可以传入 Regex 或使用 String.toRegex 以使用正则功能
117 | excludeKeys(
118 | "exclude.some.key1",
119 | "exclude.some.key2"
120 | )
121 | // 设置需要包含的属性键值名称数组
122 | // 在这里可以设置一些你希望从已知的属性键值中包含的键值名称
123 | // 这些键值在属性键值存在它们时被包含,未被包含的键值不会出现在生成的代码中
124 | // 你可以传入 Regex 或使用 String.toRegex 以使用正则功能
125 | includeKeys(
126 | "include.some.key1",
127 | "include.some.key2"
128 | )
129 | // 设置属性键值规则数组
130 | // 你可以设置一组键值规则,使用 "createValueRule" 创建新的规则,用于解析得到的键值内容
131 | // 这些键值规则在属性键值存在它们时被应用
132 | keyValuesRules(
133 | "some.key1" to createValueRule { if (it.contains("_")) it.replace("_", "-") else it },
134 | "some.key2" to createValueRule { "$it-value" }
135 | )
136 | // 设置从何处生成属性键值
137 | // 默认为 "CURRENT_PROJECT" 和 "ROOT_PROJECT"
138 | // 你可以使用以下类型来进行设置
139 | // "CURRENT_PROJECT" ← 当前项目
140 | // "ROOT_PROJECT" ← 根项目
141 | // "GLOBAL" ← 全局 (用户目录)
142 | // "SYSTEM" ← 系统
143 | // "SYSTEM_ENV" ← 系统环境变量
144 | // SweetProperty 将从你设置的生成位置依次生成属性键值,生成位置的顺序跟随你设置的顺序决定
145 | // 风险提示:"GLOBAL"、"SYSTEM"、"SYSTEM_ENV" 可能存在密钥和证书,请小心管理生成的代码
146 | generateFrom(CURRENT_PROJECT, ROOT_PROJECT)
147 | }
148 | // 项目生成代码功能
149 | // 此功能类似于 Android 项目自动生成的 BuildConfig
150 | // 在项目中生成的代码可直接被当前项目使用
151 | // 这里的配置包括 "all" 中的配置,你可以对其进行复写
152 | sourcesCode {
153 | // 自定义生成的目录路径
154 | // 你可以填写相对于当前项目的路径
155 | // 默认为 "build/generated/sweet-property"
156 | // 建议将生成的代码放置于 "build" 目录下,因为生成的代码不建议去修改它
157 | generateDirPath = "build/generated/sweet-property"
158 | // 自定义生成的包名
159 | // Android 项目默认使用 "android" 配置方法块中的 "namespace"
160 | // 普通的 Kotlin on Jvm 项目默认使用项目设置的 "project.group"
161 | // 你可以不进行设置,包名在一般情况下会自动进行匹配
162 | packageName = "com.example.mydemo"
163 | // 自定义生成的类名
164 | // 默认使用当前项目的名称 + "Properties"
165 | // 你可以不进行设置,类名在一般情况下会自动进行匹配
166 | className = "MyDemo"
167 | // 是否启用受限访问功能
168 | // 默认不启用,启用后将为生成的类和方法添加 "internal" 修饰符
169 | // 如果你的项目为工具库或依赖,通常情况下建议启用
170 | // 启用后可以防止其他开发者在引用你的库时调用到你的项目属性键值发生问题
171 | isEnableRestrictedAccess = false
172 | }
173 | // 构建脚本生成代码功能
174 | // 在构建脚本中生成的代码可直接被当前 "build.gradle.kts" 使用
175 | // 这里的配置包括 "all" 中的配置,你可以对其进行复写
176 | // 注意:Gradle 中也有一个 "buildscript" 方法块,只不过是小写的 "s",请不要混淆
177 | buildScript {
178 | // 自定义构建脚本扩展方法名称
179 | // 默认为 "property"
180 | // 你将在 "build.gradle.kts" 中使用类似 "property.some.key" 的方式进行调用
181 | extensionName = "property"
182 | }
183 | }
184 | // 根项目 (Root Project) 配置
185 | // 这是一个特殊的配置方法块,只能用于根项目
186 | rootProject {
187 | all {
188 | // 配置 "all"
189 | }
190 | sourcesCode {
191 | // 配置 "sourcesCode"
192 | }
193 | buildScript {
194 | // 配置 "buildScript"
195 | }
196 | }
197 | // 其它项目与子项目配置
198 | // 在方法参数中填入需要配置的项目完整名称来配置对应的项目
199 | // 如果当前项目是子项目,你必须填写子项目前面的 ":",例如 ":app"
200 | // 如果当前项目为嵌套型子项目,例如 app → sub
201 | // 此时你需要使用 ":" 来分隔多个子项目,例如 ":app:sub"
202 | // 注意:在 1.0.2 版本及以前是不需要添加 ":" 来标识子项目的,且添加后会报错
203 | // 这是一个错误做法,目前统一了 Gradle 的项目命名规范,请使用新的规范
204 | // 根项目的名称不能直接用来配置子项目,请使用 "rootProject"
205 | project(":app") {
206 | all {
207 | // 配置 "all"
208 | }
209 | sourcesCode {
210 | // 配置 "sourcesCode"
211 | }
212 | buildScript {
213 | // 配置 "buildScript"
214 | }
215 | }
216 | // 同时进行多个项目与子项目配置
217 | // 在方法参数中填入需要配置的项目完整名称数组来配置每个对应的项目
218 | project(":modules:library1", ":modules:library2") {
219 | all {
220 | // 配置 "all"
221 | }
222 | sourcesCode {
223 | // 配置 "sourcesCode"
224 | }
225 | buildScript {
226 | // 配置 "buildScript"
227 | }
228 | }
229 | }
230 | ```
231 |
232 | 如需在 Groovy DSL 中使用,请将所有变量的 `=` 改为空格,并删除 `Enable` 前方的 `is` 并将 `E` 小写即可。
233 |
234 | 如果你遇到了 `Gradle DSL method not found` 错误,解决方案为迁移到 Kotlin DSL。
235 |
236 | 如果你不想全部使用 Kotlin DSL,你也可以仅将 `settings.gradle` 迁移到 `settings.gradle.kts`。
237 |
238 | ## 使用示例
239 |
240 | 下面是一个项目的 `gradle.properties` 配置文件。
241 |
242 | ```properties
243 | project.groupName=com.highcapable.sweetpropertydemo
244 | project.description=Hello SweetProperty Demo!
245 | project.version=1.0.0
246 | ```
247 |
248 | 在构建脚本 `build.gradle.kts` 中,我们就可以如下所示这样直接去使用这些键值。
249 |
250 | 这里以 Maven 发布的配置部分举例。
251 |
252 | ```kotlin
253 | publications {
254 | create("maven") {
255 | groupId = property.project.groupName
256 | version = property.project.version
257 | pom.description.set(property.project.description)
258 | from(components["java"])
259 | }
260 | }
261 | ```
262 |
263 | 同样地,你也可以在当前项目中调用生成的键值。
264 |
265 | ```kotlin
266 | SweetPropertyDemoProperties.PROJECT_GROUP_NAME
267 | SweetPropertyDemoProperties.PROJECT_DESCRIPTION
268 | SweetPropertyDemoProperties.PROJECT_VERSION
269 | ```
270 |
271 | 下面再以 Android 项目举例。
272 |
273 | 在 Android 项目中通常需要配置很多重复、固定的属性,例如 `targetSdk`。
274 |
275 | ```properties
276 | project.compileSdk=33
277 | project.targetSdk=33
278 | project.minSdk=22
279 | ```
280 |
281 | 当你设置了 `isEnableTypeAutoConversion = true` 时,`SweetProperty` 在生成实体类过程在默认配置下将尝试将其转换为对应的类型。
282 |
283 | 例如下方所使用的键值,其类型可被识别为整型,可被项目配置直接使用。
284 |
285 | ```kotlin
286 | android {
287 | compileSdk = property.project.compileSdk
288 | defaultConfig {
289 | minSdk = property.project.minSdk
290 | targetSdk = property.project.targetSdk
291 | }
292 | }
293 | ```
294 |
295 | 你可以无需再使用 `buildConfigField` 向 `BuildConfig` 添加代码,有了 `SweetProperty` 生成的属性键值代码,你可以更加灵活地管理你的项目。
296 |
297 | 你还可以在属性键值中使用 `${...}` 互相引用其中的内容,但不允许递归引用。
298 |
299 | 当你设置了 `isEnableValueInterpolation = true` 时,`SweetProperty` 将自动合并这些引用的内容到对应位置。
300 |
301 | ```properties
302 | project.name=MyDemo
303 | project.developer.name=myname
304 | project.url=https://github.com/${project.developer.name}/${project.name}
305 | ```
306 |
307 | 注意:这个特性是 `SweetProperty` 提供的,原生的 `gradle.properties` 并不支持此功能。
308 |
309 | **可能遇到的问题**
310 |
311 | 如果你的项目仅存在一个根项目,且没有导入任何子项目,此时如果扩展方法不能正常生成,
312 | 你可以将你的根项目迁移至子项目并在 `settings.gradle.kts` 中导入这个子项目,这样即可解决此问题。
313 |
314 | 我们一般推荐将项目的功能进行分类,根项目仅用来管理插件和一些配置。
315 |
316 | **局限性说明**
317 |
318 | `SweetProperty` 无法生成 `settings.gradle.kts` 中的扩展方法,因为这属于 `SweetProperty` 的上游。
319 |
320 | ## 问题反馈
321 |
322 | 如果你在使用 `SweetProperty` 的过程中遇到了任何问题,你都可以随时在 GitHub 开启一个 `issues` 向我们反馈。
--------------------------------------------------------------------------------
/docs/guide.md:
--------------------------------------------------------------------------------
1 | # Sweet Property Documentation
2 |
3 | Before you start using it, it is recommended that you read this document carefully so that you can better understand how it works and its functions.
4 |
5 | If your project is still managed using Groovy DSL, it is recommended to migrate to Kotlin DSL.
6 |
7 | We will no longer be responsible for troubleshooting and fixing any issues that occur with this plugin in Groovy DSL,
8 | and Groovy DSL support may be dropped entirely in later releases.
9 |
10 | Note: Usage in the Groovy DSL will not be detailed in this document.
11 |
12 | ## Prerequisites
13 |
14 | Please note that `SweetProperty` is recommended to be loaded using the new `pluginManagement` method, which is a feature added since Gradle `7.x.x`
15 | version.
16 |
17 | If your project is still managed using the `buildscript` method, it is recommended to migrate to the new method, and the instructions for using the
18 | old version will no longer be provided here.
19 |
20 | ## Quick Start
21 |
22 | First, open `settings.gradle.kts` in your root project.
23 |
24 | Add the following code to `settings.gradle.kts` in your root project.
25 |
26 | If `pluginManagement` already exists, there is no need to add it again.
27 |
28 | > The following example
29 |
30 | ```kotlin
31 | pluginManagement {
32 | repositories {
33 | gradlePluginPortal()
34 | google()
35 | mavenCentral()
36 | }
37 | }
38 | plugins {
39 | id("com.highcapable.sweetproperty") version ""
40 | }
41 | ```
42 |
43 | Please replace `` in the above code with the latest version in
44 | [Release](https://github.com/HighCapable/SweetProperty/releases), please note **DO NOT** add `apply false` after.
45 |
46 | After the above configuration is completed, run Gradle Sync once.
47 |
48 | ## Function Configuration
49 |
50 | You can configure `SweetProperty` to achieve custom and personalized functions.
51 |
52 | `SweetProperty` provides you with a relatively rich set of customizable functions.
53 |
54 | The following are the descriptions and configuration methods of these functions.
55 |
56 | Please add `sweetProperty` method block to your `settings.gradle.kts` to start configuring `SweetProperty`.
57 |
58 | > The following example
59 |
60 | ```kotlin
61 | sweetProperty {
62 | // Enable SweetProperty, set to false will disable all functions
63 | isEnable = true
64 | // Global configuration
65 | // You can modify the configuration in all projects in the global configuration
66 | // Configurations that are not declared in each project will use the global configuration
67 | // The functions in each configuration method block are exactly the same
68 | // You can refer to the configuration method of the root project and sub-projects below
69 | global {
70 | // General code generation function
71 | // Here you can configure the related functions of the build script and the project generated code at the same time
72 | all {
73 | // Enable functionality
74 | // You can set "sourcesCode" and "buildScript" respectively
75 | isEnable = true
76 | // Whether to enable the exclusion of non-string type key-values content
77 | // Enabled by default, when enabled, key-values and content that are not string types will be excluded from the properties key-values
78 | // This can exclude e.g. configuration of some system environment variables or data in memory
79 | isEnableExcludeNonStringValue = true
80 | // Whether to enable the type automatic conversion function
81 | // Enabled by default, when enabled,
82 | // the type in the properties key-values will be automatically recognized and converted to the corresponding type
83 | // After enabling, if you want to force the content of a value to be a string type,
84 | // you can use single quotes or double quotes to wrap the entire string
85 | // Note: After turning off this function, the functions mentioned above will also become invalid at the same time
86 | // For example name=hello and number=1 they will be automatically converted to String and Int
87 | // For example stringNumber="1" or stringNumber='1' they will be coerced to String
88 | isEnableTypeAutoConversion = true
89 | // Whether to enable key-values content interpolation
90 | // Enabled by default, after enabling, the ${...} content in the properties key-values content
91 | // will be automatically recognized and replaced
92 | // Note: The interpolated content will only be searching from the current (current configuration file) properties key-values list
93 | isEnableValueInterpolation = true
94 | // Set properties names array
95 | // The properties file will be automatically obtained from the root directory of
96 | // the current root project, subproject and user directory according to the file name you set
97 | // You can add multiple sets of properties file names, which will be read in order
98 | // In general, you don't need to modify this setting, the wrong file name will lead to getting empty key-values content
99 | // You can configure the "isAddDefault" parameter to decide whether to add the default "gradle.properties" file name
100 | // If you have one or more properties files with custom names, you can modify the settings here
101 | // Note: It is recommended to configure each project individually, rather than modifying globally, in case of problems
102 | propertiesFileNames(
103 | "some_other_1.properties",
104 | "some_other_2.properties",
105 | isAddDefault = true
106 | )
107 | // Set fixed properties key-values array
108 | // Here you can set some key values that must exist,
109 | // and these key values will be generated regardless of whether they can be obtained from the properties key-values
110 | // These key-values use the content of the properties key-values when the properties key-values exists,
111 | // and use the content set here when it does not exist
112 | // Note: There cannot be special symbols and spaces in the attribute key value name, otherwise the generation may fail
113 | // For example: you need to obtain the key or certificate stored on your own devices,
114 | // but these keys are not available on other people's devices
115 | // At this point this function will become very useful, you can set the default value when there are no these key-values
116 | // Of course, you can also use this function to forcefully add additional properties key-values to the generated code
117 | permanentKeyValues(
118 | "permanent.some.key1" to "some_value_1",
119 | "permanent.some.key2" to "some_value_2"
120 | )
121 | // Set properties key-values array names that need to be excluded
122 | // Here you can set some key names that you want to exclude from the known properties keys
123 | // These keys are excluded when they exist in the properties keys and will not appear in the generated code
124 | // Note: If you exclude the key-values set in "permanentKeyValues",
125 | // then they will only become the initial key-values content you set and continue to exist
126 | // You can pass in a Regex or use String.toRegex to use the regex function
127 | excludeKeys(
128 | "exclude.some.key1",
129 | "exclude.some.key2"
130 | )
131 | // Set properties key-values array names that need to be included
132 | // Here you can set some key-values names that you want to include from known properties keys
133 | // These keys are included when the properties key-values exists
134 | // Key-values that are not included will not appear in the generated code
135 | // You can pass in a Regex or use String.toRegex to use the regex function
136 | includeKeys(
137 | "include.some.key1",
138 | "include.some.key2"
139 | )
140 | // Set properties key-values rules array
141 | // You can set a set of key-values rules and use "createValueRule" to create new rules for parsing the obtained value content
142 | // These key-values rules are applied when the properties key-values exists
143 | keyValuesRules(
144 | "some.key1" to createValueRule { if (it.contains("_")) it.replace("_", "-") else it },
145 | "some.key2" to createValueRule { "$it-value" }
146 | )
147 | // Set where to generate properties key-values
148 | // Defaults to "CURRENT_PROJECT" and "ROOT_PROJECT"
149 | // You can use the following types to set
150 | // "CURRENT_PROJECT" ← Current project
151 | // "ROOT_PROJECT" ← Root project
152 | // "GLOBAL" ← Global (user directory)
153 | // "SYSTEM" ← System
154 | // "SYSTEM_ENV" ← System environment variable
155 | // SweetProperty will generate properties key-values sequentially from the generation positions you set,
156 | // and the order of generation positions is determined by the order you set
157 | // Pay Attention: "GLOBAL", "SYSTEM", "SYSTEM_ENV" may have keys and certificates, please manage the generated code carefully
158 | generateFrom(CURRENT_PROJECT, ROOT_PROJECT)
159 | }
160 | // Project generation code function
161 | // This function is similar to the BuildConfig automatically generated by Android projects
162 | // The code generated in the project can be directly used by the current project
163 | // The configuration here includes the configuration in "all", you can override it
164 | sourcesCode {
165 | // Custom generated directory path
166 | // You can fill in the path relative to the current project
167 | // Defaults to "build/generated/sweet-property"
168 | // It is recommended to place the generated code in the "build" directory,
169 | // because the generated code is not recommended to be modified
170 | generateDirPath = "build/generated/sweet-property"
171 | // Custom generated package name
172 | // Android projects use the "namespace" in the "android" configuration method block by default
173 | // Ordinary Kotlin on Jvm projects use the "project.group" of the project settings by default
174 | // You don’t need to set it, the package name will automatically match under normal circumstances
175 | packageName = "com.example.mydemo"
176 | // Custom generated class name
177 | // By default, the name of the current project + "Properties" is used
178 | // You don't need to set it, the class name will be automatically matched under normal circumstances
179 | className = "MyDemo"
180 | // Whether to enable restricted access
181 | // Not enabled by default
182 | // When enabled, the "internal" modifier will be added to the generated classes and methods
183 | // If your project is a tool library or dependency, it is usually recommended to enable it
184 | // Once enabled, it can prevent other developers from calling your project properties key-values when referencing your library
185 | isEnableRestrictedAccess = false
186 | }
187 | // Build script generate code function
188 | // The code generated in the build script can be directly used by the current "build.gradle.kts"
189 | // The configuration here includes the configuration in "all", you can override it
190 | // Note: There is also a "buildscript" method block in Gradle, but it is just a lowercase "s", please don't confuse it
191 | buildScript {
192 | // Custom build script extension method name
193 | // Defaults to "property"
194 | // You will call it in "build.gradle.kts" using something like "property.some.key"
195 | extensionName = "property"
196 | }
197 | }
198 | // Root project configuration
199 | // This is a special configuration method block that can only be used in root projects
200 | rootProject {
201 | all {
202 | // Configure "all"
203 | }
204 | sourcesCode {
205 | // Configure "sourcesCode"
206 | }
207 | buildScript {
208 | // Configure "buildScript"
209 | }
210 | }
211 | // Other projects and sub-projects configurations
212 | // Fill in the full name of the project that needs to be configured in the method parameters to configure the corresponding project
213 | // If the current project is a sub-project, you must fill in the ":" in front of the sub-project, such as ":app"
214 | // If the current project is a nested sub-project, such as app → sub
215 | // At this time you need to use ":" to separate multiple sub-projects, such as ":app:sub"
216 | // Note: In version 1.0.2 and before, there is no need to add ":" to identify sub-projects, and an error will be thrown after adding it
217 | // This is a wrong approach, Gradle's project naming convention is currently unified, please use the new convention
218 | // The name of the root project cannot be used directly to configure sub-projects, please use "rootProject"
219 | project(":app") {
220 | all {
221 | // Configure "all"
222 | }
223 | sourcesCode {
224 | // Configure "sourcesCode"
225 | }
226 | buildScript {
227 | // Configure "buildScript"
228 | }
229 | }
230 | // Configure multiple projects and sub-projects at the same time
231 | // Fill in the method parameters with the array of complete names of the projects that need to be configured
232 | // to configure each corresponding project
233 | project(":modules:library1", ":modules:library2") {
234 | all {
235 | // Configure "all"
236 | }
237 | sourcesCode {
238 | // Configure "sourcesCode"
239 | }
240 | buildScript {
241 | // Configure "buildScript"
242 | }
243 | }
244 | ```
245 |
246 | If you want to use it in Groovy DSL, please change the `=` of all variables to spaces, delete the `is` in front of `Enable` and lowercase `E`.
247 |
248 | If you encounter `Gradle DSL method not found` error, the solution is to migrate to Kotlin DSL.
249 |
250 | If you don't want to use the Kotlin DSL entirely, you can also migrate just `settings.gradle` to `settings.gradle.kts`.
251 |
252 | ## Usage Example
253 |
254 | Below is a project's `gradle.properties` configuration file.
255 |
256 | ```properties
257 | project.groupName=com.highcapable.sweetpropertydemo
258 | project.description=Hello SweetProperty Demo!
259 | project.version=1.0.0
260 | ```
261 |
262 | In the build script `build.gradle.kts`, we can use these keys directly as shown below.
263 |
264 | Here is an example of the configuration part published by Maven.
265 |
266 | ```kotlin
267 | publications {
268 | create("maven") {
269 | groupId = property.project.groupName
270 | version = property.project.version
271 | pom.description.set(property.project.description)
272 | from(components["java"])
273 | }
274 | }
275 | ```
276 |
277 | Similarly, you can also call the generated key-value in the current project.
278 |
279 | ```kotlin
280 | SweetPropertyDemoProperties.PROJECT_GROUP_NAME
281 | SweetPropertyDemoProperties.PROJECT_DESCRIPTION
282 | SweetPropertyDemoProperties.PROJECT_VERSION
283 | ```
284 |
285 | Let's take the Android project as an example.
286 |
287 | In Android projects, it is usually necessary to configure many repeated and fixed properties, such as `targetSdk`.
288 |
289 | ```properties
290 | project.compileSdk=33
291 | project.targetSdk=33
292 | project.minSdk=22
293 | ```
294 |
295 | When you set the `isEnableTypeAutoConversion = true`, `SweetProperty` will try to convert it to the corresponding type under the
296 | default configuration during the process of generating the entity class.
297 |
298 | For example, the type of the key-value used below can be recognized as an integer and can be directly used by the project configuration.
299 |
300 | ```kotlin
301 | android {
302 | compileSdk = property.project.compileSdk
303 | defaultConfig {
304 | minSdk = property.project.minSdk
305 | targetSdk = property.project.targetSdk
306 | }
307 | }
308 | ```
309 |
310 | You no longer need to use `buildConfigField` to add code to `BuildConfig`,
311 | with the properties key-values code generated by `SweetProperty`, you can manage your project more flexibly.
312 |
313 | You can also use `${...}` in properties values to refer to each other, but recursive references are not allowed.
314 |
315 | When you set the `isEnableValueInterpolation = true`,
316 | `SweetProperty` will automatically merge the contents of these references into the corresponding locations.
317 |
318 | ```properties
319 | project.name=MyDemo
320 | project.developer.name=myname
321 | project.url=https://github.com/${project.developer.name}/${project.name}
322 | ```
323 |
324 | Note: This feature is provided by `SweetProperty`, and the native `gradle.properties` does not support this feature.
325 |
326 | **Possible Problems**
327 |
328 | If your project only has one root project and does not import any sub-projects,
329 | if extension methods are not generated properly,
330 | you can solve this problem by migrating your root project to a sub-projects and importing this sub-projects in `settings.gradle.kts`.
331 |
332 | We generally recommend classifying the functions of the project, and the root project is only used to manage plugins and some configurations.
333 |
334 | **Limitations Note**
335 |
336 | `SweetProperty` cannot generated extension methods in `settings.gradle.kts`, because this belongs to the upstream of `SweetProperty`.
337 |
338 | ## Feedback
339 |
340 | If you encounter any problems while using `SweetProperty`, you can always open an `issues` on GitHub to give us feedback.
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project Configuration
2 | project.name=SweetProperty
3 | project.url=https://github.com/HighCapable/SweetProperty
4 | project.groupName=com.highcapable.sweetproperty
5 | project.moduleName=sweet-property
6 | project.version=1.0.5
7 | # Gradle Plugin Configuration
8 | gradle.plugin.moduleName=${project.groupName}.gradle.plugin
9 | gradle.plugin.implementationClass=${project.groupName}.plugin.SweetPropertyPlugin
10 | # Maven Publish Configuration
11 | SONATYPE_HOST=S01
12 | RELEASE_SIGNING_ENABLED=true
13 | # Maven POM Configuration
14 | POM_NAME=SweetProperty
15 | POM_ARTIFACT_ID=sweet-property
16 | POM_DESCRIPTION=An easy get project properties anywhere Gradle plugin.
17 | POM_URL=https://github.com/HighCapable/SweetProperty
18 | POM_LICENSE_NAME=Apache License 2.0
19 | POM_LICENSE_URL=https://github.com/HighCapable/SweetProperty/blob/master/LICENSE
20 | POM_LICENSE_DIST=repo
21 | POM_SCM_URL=https://github.com/HighCapable/SweetProperty
22 | POM_SCM_CONNECTION=scm:git:git://github.com/HighCapable/SweetProperty
23 | POM_SCM_DEV_CONNECTION=scm:git:ssh://github.com/HighCapable/SweetProperty
24 | POM_DEVELOPER_ID=0
25 | POM_DEVELOPER_NAME=fankes
26 | POM_DEVELOPER_EMAIL=qzmmcn@163.com
27 | POM_DEVELOPER_URL=https://github.com/fankes
--------------------------------------------------------------------------------
/gradle/sweet-dependency/sweet-dependency-config.yaml:
--------------------------------------------------------------------------------
1 | preferences:
2 | autowire-on-sync-mode: UPDATE_OPTIONAL_DEPENDENCIES
3 | repositories-mode: FAIL_ON_PROJECT_REPOS
4 |
5 | repositories:
6 | gradle-plugin-portal:
7 | scope: PLUGINS
8 | google:
9 | maven-central:
10 | maven-local:
11 |
12 | plugins:
13 | org.jetbrains.kotlin.jvm:
14 | alias: kotlin-jvm
15 | version: 2.1.10
16 | com.vanniktech.maven.publish:
17 | alias: maven-publish
18 | version: 0.31.0
19 |
20 | libraries:
21 | com.squareup.okhttp3:
22 | okhttp:
23 | version: 4.12.0
24 | com.squareup:
25 | kotlinpoet:
26 | version: 2.1.0
27 | javapoet:
28 | version: 1.13.0
29 | net.lingala.zip4j:
30 | zip4j:
31 | version: 2.11.5
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HighCapable/SweetProperty/9e7d5ec5745b23f1cc3ac1235900131f7a6cc0f8/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/img-src/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HighCapable/SweetProperty/9e7d5ec5745b23f1cc3ac1235900131f7a6cc0f8/img-src/icon.png
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | mavenLocal()
7 | }
8 | }
9 | plugins {
10 | id("com.highcapable.sweetdependency") version "1.0.4"
11 | id("com.highcapable.sweetproperty") version "1.0.5"
12 | }
13 | sweetDependency {
14 | isEnableVerboseMode = false
15 | }
16 | sweetProperty {
17 | global {
18 | sourcesCode {
19 | className = rootProject.name
20 | includeKeys(
21 | "^project\\..*\$".toRegex(),
22 | "^gradle\\..*\$".toRegex()
23 | )
24 | isEnableRestrictedAccess = true
25 | }
26 | }
27 | rootProject { all { isEnable = false } }
28 | }
29 | rootProject.name = "SweetProperty"
30 | include(":sweetproperty-gradle-plugin")
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | autowire(libs.plugins.kotlin.jvm)
4 | autowire(libs.plugins.maven.publish)
5 | }
6 |
7 | group = property.project.groupName
8 | version = property.project.version
9 |
10 | java {
11 | sourceCompatibility = JavaVersion.VERSION_21
12 | targetCompatibility = JavaVersion.VERSION_21
13 | withSourcesJar()
14 | }
15 |
16 | kotlin {
17 | jvmToolchain(21)
18 | sourceSets.all { languageSettings { languageVersion = "2.0" } }
19 | compilerOptions {
20 | freeCompilerArgs = listOf(
21 | "-Xno-param-assertions",
22 | "-Xno-call-assertions",
23 | "-Xno-receiver-assertions"
24 | )
25 | }
26 | }
27 |
28 | dependencies {
29 | implementation(com.squareup.okhttp3.okhttp)
30 | implementation(com.squareup.kotlinpoet)
31 | implementation(com.squareup.javapoet)
32 | implementation(net.lingala.zip4j.zip4j)
33 | }
34 |
35 | gradlePlugin {
36 | plugins {
37 | create(property.project.moduleName) {
38 | id = property.project.groupName
39 | implementationClass = property.gradle.plugin.implementationClass
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/SweetProperty.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/25.
21 | */
22 | package com.highcapable.sweetproperty
23 |
24 | import com.highcapable.sweetproperty.generated.SweetPropertyProperties
25 |
26 | /**
27 | * [SweetProperty] 的装载调用类
28 | */
29 | object SweetProperty {
30 |
31 | /** 标签名称 */
32 | const val TAG = SweetPropertyProperties.PROJECT_NAME
33 |
34 | /** 版本 */
35 | const val VERSION = SweetPropertyProperties.PROJECT_VERSION
36 |
37 | /** 项目地址 */
38 | const val PROJECT_URL = SweetPropertyProperties.PROJECT_URL
39 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/gradle/entity/ProjectDescriptor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/28.
21 | */
22 | package com.highcapable.sweetproperty.gradle.entity
23 |
24 | import com.highcapable.sweetproperty.gradle.factory.fullName
25 | import com.highcapable.sweetproperty.utils.debug.SError
26 | import com.highcapable.sweetproperty.utils.noBlank
27 | import org.gradle.api.Project
28 | import org.gradle.api.initialization.Settings
29 | import java.io.File
30 | import kotlin.properties.Delegates
31 |
32 | /**
33 | * 项目描述实现类
34 | */
35 | internal class ProjectDescriptor private constructor() {
36 |
37 | internal companion object {
38 |
39 | /**
40 | * 创建 [ProjectDescriptor]
41 | * @param settings 当前设置
42 | * @param name 当前名称 (项目) - 默认空
43 | * @return [ProjectDescriptor]
44 | */
45 | internal fun create(settings: Settings, name: String = "") = ProjectDescriptor().also {
46 | val isRootProject = name.isBlank() || name.lowercase() == settings.rootProject.name.lowercase()
47 | val subProjectNotice = "if this is a sub-project, please set it like \":$name\""
48 | it.type = Type.SETTINGS
49 | it.name = name.noBlank() ?: settings.rootProject.name
50 | it.currentDir = (if (isRootProject) settings.rootProject else settings.findProject(name))?.projectDir
51 | ?: SError.make("Project \"$name\" not found${if (!name.startsWith(":")) ", $subProjectNotice" else ""}")
52 | it.rootDir = settings.rootDir
53 | it.homeDir = settings.gradle.gradleUserHomeDir
54 | }
55 |
56 | /**
57 | * 创建 [ProjectDescriptor]
58 | * @param project 当前项目
59 | * @return [ProjectDescriptor]
60 | */
61 | internal fun create(project: Project) = ProjectDescriptor().also {
62 | it.type = Type.PROJECT
63 | it.name = project.fullName()
64 | it.currentDir = project.projectDir
65 | it.rootDir = project.rootDir
66 | it.homeDir = project.gradle.gradleUserHomeDir
67 | }
68 | }
69 |
70 | /** 当前项目类型 */
71 | internal var type by Delegates.notNull()
72 |
73 | /** 当前项目名称 */
74 | internal var name = ""
75 |
76 | /** 当前项目目录 */
77 | internal var currentDir by Delegates.notNull()
78 |
79 | /** 根项目目录 */
80 | internal var rootDir by Delegates.notNull()
81 |
82 | /** 用户目录 */
83 | internal var homeDir by Delegates.notNull()
84 |
85 | /**
86 | * 项目类型定义类
87 | */
88 | internal enum class Type {
89 | /** 设置 */
90 | SETTINGS,
91 |
92 | /** 项目 */
93 | PROJECT
94 | }
95 |
96 | override fun toString() = "ProjectDescriptor(type: $type, name: $name)"
97 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/gradle/factory/ExtensionAwareFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | @file:Suppress("unused", "USELESS_CAST", "KotlinRedundantDiagnosticSuppress")
23 |
24 | package com.highcapable.sweetproperty.gradle.factory
25 |
26 | import com.highcapable.sweetproperty.utils.camelcase
27 | import com.highcapable.sweetproperty.utils.debug.SError
28 | import org.gradle.api.Action
29 | import org.gradle.api.plugins.ExtensionAware
30 |
31 | /**
32 | * 创建、获取扩展方法
33 | * @param name 方法名称 - 自动调用 [toSafeExtName]
34 | * @param clazz 目标对象 [Class]
35 | * @param args 方法参数
36 | * @return [ExtensionAware]
37 | */
38 | internal fun ExtensionAware.getOrCreate(name: String, clazz: Class<*>, vararg args: Any?) = name.toSafeExtName().let { sName ->
39 | runCatching { extensions.create(sName, clazz, *args).asExtension() }.getOrElse {
40 | if (!(it is IllegalArgumentException && it.message?.startsWith("Cannot add extension with name") == true)) throw it
41 | runCatching { extensions.getByName(sName).asExtension() }.getOrNull() ?: SError.make("Create or get extension failed with name \"$sName\"")
42 | }
43 | }
44 |
45 | /**
46 | * 创建、获取扩展方法 - 目标对象 [T]
47 | * @param name 方法名称 - 自动调用 [toSafeExtName]
48 | * @param args 方法参数
49 | * @return [T]
50 | */
51 | internal inline fun ExtensionAware.getOrCreate(name: String, vararg args: Any?) = name.toSafeExtName().let { sName ->
52 | runCatching { extensions.create(sName, T::class.java, *args) as T }.getOrElse {
53 | if (!(it is IllegalArgumentException && it.message?.startsWith("Cannot add extension with name") == true)) throw it
54 | runCatching { extensions.getByName(sName) as? T? }.getOrNull() ?: SError.make("Create or get extension failed with name \"$sName\"")
55 | }
56 | }
57 |
58 | /**
59 | * 获取扩展方法
60 | * @param name 方法名称
61 | * @return [ExtensionAware]
62 | */
63 | internal fun ExtensionAware.get(name: String) =
64 | runCatching { extensions.getByName(name).asExtension() }.getOrNull() ?: SError.make("Could not get extension with name \"$name\"")
65 |
66 | /**
67 | * 获取扩展方法 - 目标对象 [T]
68 | * @param name 方法名称
69 | * @return [T]
70 | */
71 | internal inline fun ExtensionAware.get(name: String) =
72 | runCatching { extensions.getByName(name) as T }.getOrNull() ?: SError.make("Could not get extension with name \"$name\"")
73 |
74 | /**
75 | * 获取扩展方法 - 目标对象 [T]
76 | * @return [T]
77 | */
78 | internal inline fun ExtensionAware.get() =
79 | runCatching { extensions.getByType(T::class.java) as T }.getOrNull() ?: SError.make("Could not get extension with type ${T::class.java}")
80 |
81 | /**
82 | * 配置扩展方法 - 目标对象 [T]
83 | * @param name 方法名称
84 | * @param configure 配置方法体
85 | */
86 | internal inline fun ExtensionAware.configure(name: String, configure: Action) = extensions.configure(name, configure)
87 |
88 | /**
89 | * 是否存在扩展方法
90 | * @param name 方法名称
91 | * @return [Boolean]
92 | */
93 | internal fun ExtensionAware.hasExtension(name: String) = runCatching { extensions.getByName(name); true }.getOrNull() ?: false
94 |
95 | /**
96 | * 转换到扩展方法类型 [ExtensionAware]
97 | * @return [ExtensionAware]
98 | * @throws IllegalStateException 如果类型不是 [ExtensionAware]
99 | */
100 | internal fun Any.asExtension() = this as? ExtensionAware? ?: SError.make("This instance \"$this\" is not a valid Extension")
101 |
102 | /**
103 | * 由于 Gradle 存在一个 [ExtensionAware] 的扩展
104 | *
105 | * 此功能用于检测当前字符串是否为 Gradle 使用的关键字名称
106 | * @return [Boolean]
107 | */
108 | internal fun String.isUnSafeExtName() = camelcase().let { it == "ext" || it == "extra" || it == "extraProperties" || it == "extensions" }
109 |
110 | /**
111 | * 由于 Gradle 存在一个 [ExtensionAware] 的扩展
112 | *
113 | * 此功能用于转换不符合规定的字符串到 "{字符串}s"
114 | * @return [String]
115 | */
116 | internal fun String.toSafeExtName() = if (isUnSafeExtName()) "${this}s" else this
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/gradle/factory/GradleProjectFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.gradle.factory
23 |
24 | import com.highcapable.sweetproperty.utils.code.entity.MavenPomData
25 | import com.highcapable.sweetproperty.utils.toFile
26 | import org.gradle.api.Project
27 | import org.gradle.kotlin.dsl.buildscript
28 | import org.gradle.kotlin.dsl.repositories
29 |
30 | /**
31 | * 获取指定项目的完整名称
32 | * @param isUseColon 是否在子项目前使用冒号 - 默认是
33 | * @return [String]
34 | */
35 | internal fun Project.fullName(isUseColon: Boolean = true): String {
36 | val isRoot = this == rootProject
37 | val baseNames = mutableListOf()
38 |
39 | /**
40 | * 递归子项目
41 | * @param project 当前项目
42 | */
43 | fun fetchChild(project: Project) {
44 | project.parent?.also { if (it != it.rootProject) fetchChild(it) }
45 | baseNames.add(project.name)
46 | }; fetchChild(project = this)
47 | return buildString { baseNames.onEach { append(":$it") }.clear() }.let { if (isUseColon && !isRoot) it else it.drop(1) }
48 | }
49 |
50 | /**
51 | * 向构建脚本添加自定义依赖
52 | * @param repositoryPath 存储库路径
53 | * @param pomData Maven POM 实体
54 | */
55 | internal fun Project.addDependencyToBuildScript(repositoryPath: String, pomData: MavenPomData) =
56 | buildscript {
57 | repositories {
58 | maven {
59 | url = repositoryPath.toFile().toURI()
60 | mavenContent { includeGroup(pomData.groupId) }
61 | }
62 | }; dependencies { classpath("${pomData.groupId}:${pomData.artifactId}:${pomData.version}") }
63 | }
64 |
65 | /**
66 | * 装载构建脚本的 [Class]
67 | * @param name [Class] 完整名称
68 | * @return [Class]
69 | */
70 | internal fun Project.loadBuildScriptClass(name: String) = runCatching { buildscript.classLoader.loadClass(name) }.getOrNull()
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/gradle/proxy/IGradleLifecycle.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.gradle.proxy
23 |
24 | import org.gradle.api.Project
25 | import org.gradle.api.initialization.Settings
26 |
27 | /**
28 | * Gradle 生命周期接口
29 | */
30 | internal interface IGradleLifecycle {
31 |
32 | /**
33 | * 当 Gradle 开始装载时回调
34 | * @param settings 当前设置
35 | */
36 | fun onSettingsLoaded(settings: Settings)
37 |
38 | /**
39 | * 当 Gradle 装载完成时回调
40 | * @param settings 当前设置
41 | */
42 | fun onSettingsEvaluate(settings: Settings)
43 |
44 | /**
45 | * 当 Gradle 开始装载项目时回调
46 | * @param rootProject 当前根项目
47 | */
48 | fun onProjectLoaded(rootProject: Project)
49 |
50 | /**
51 | * 当 Gradle 项目装载完成时回调
52 | * @param rootProject 当前根项目
53 | */
54 | fun onProjectEvaluate(rootProject: Project)
55 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/SweetPropertyExtension.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.plugin
23 |
24 | import com.highcapable.sweetproperty.SweetProperty
25 | import com.highcapable.sweetproperty.gradle.factory.getOrCreate
26 | import com.highcapable.sweetproperty.gradle.proxy.IGradleLifecycle
27 | import com.highcapable.sweetproperty.plugin.extension.dsl.configure.SweetPropertyConfigureExtension
28 | import com.highcapable.sweetproperty.plugin.helper.PluginUpdateHelper
29 | import com.highcapable.sweetproperty.plugin.helper.PropertiesDeployHelper
30 | import com.highcapable.sweetproperty.utils.debug.SError
31 | import org.gradle.api.Project
32 | import org.gradle.api.initialization.Settings
33 |
34 | /**
35 | * [SweetProperty] 插件扩展类
36 | */
37 | internal class SweetPropertyExtension internal constructor() : IGradleLifecycle {
38 |
39 | /** 当前配置方法体实例 */
40 | private var configure: SweetPropertyConfigureExtension? = null
41 |
42 | override fun onSettingsLoaded(settings: Settings) {
43 | configure = settings.getOrCreate(SweetPropertyConfigureExtension.NAME)
44 | }
45 |
46 | override fun onSettingsEvaluate(settings: Settings) {
47 | val configs = configure?.build(settings) ?: SError.make("Extension \"${SweetPropertyConfigureExtension.NAME}\" create failed")
48 | PluginUpdateHelper.checkingForUpdate(settings)
49 | PropertiesDeployHelper.initialize(settings, configs)
50 | }
51 |
52 | override fun onProjectLoaded(rootProject: Project) {
53 | PropertiesDeployHelper.resolve(rootProject)
54 | }
55 |
56 | override fun onProjectEvaluate(rootProject: Project) {
57 | PropertiesDeployHelper.deploy(rootProject)
58 | }
59 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/SweetPropertyPlugin.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/25.
21 | */
22 | @file:Suppress("unused")
23 |
24 | package com.highcapable.sweetproperty.plugin
25 |
26 | import com.highcapable.sweetproperty.SweetProperty
27 | import com.highcapable.sweetproperty.utils.debug.SError
28 | import org.gradle.api.Plugin
29 | import org.gradle.api.initialization.Settings
30 | import org.gradle.api.plugins.ExtensionAware
31 |
32 | /**
33 | * [SweetProperty] 插件定义类
34 | */
35 | class SweetPropertyPlugin internal constructor() : Plugin {
36 |
37 | /** 当前扩展实例 */
38 | private val extension = SweetPropertyExtension()
39 |
40 | override fun apply(target: T) = when (target) {
41 | is Settings -> {
42 | extension.onSettingsLoaded(target)
43 | target.gradle.settingsEvaluated { extension.onSettingsEvaluate(target) }
44 | target.gradle.projectsLoaded {
45 | rootProject.beforeEvaluate { extension.onProjectLoaded(rootProject = this) }
46 | rootProject.afterEvaluate { extension.onProjectEvaluate(rootProject = this) }
47 | }
48 | }
49 | else -> SError.make("${SweetProperty.TAG} can only applied in settings.gradle or settings.gradle.kts, but current is $target")
50 | }
51 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/config/default/DefaultConfigs.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/28.
21 | */
22 | package com.highcapable.sweetproperty.plugin.config.default
23 |
24 | import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
25 | import com.highcapable.sweetproperty.plugin.extension.dsl.configure.SweetPropertyConfigureExtension
26 | import com.highcapable.sweetproperty.plugin.generator.factory.PropertyValueRule
27 |
28 | /**
29 | * 默认配置类实现类
30 | */
31 | internal object DefaultConfigs {
32 |
33 | /**
34 | * 获取默认子配置类
35 | * @param name 名称
36 | * @param base 继承配置 - 默认 null
37 | * @return [ISweetPropertyConfigs.ISubConfigs]
38 | */
39 | internal fun subConfigs(name: String, base: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null) =
40 | object : ISweetPropertyConfigs.ISubConfigs {
41 | override val sourcesCode get() = sourcesCodeGenerateConfigs(name, base)
42 | override val buildScript get() = buildScriptGenerateConfigs(name, base)
43 | }
44 |
45 | /**
46 | * 获取默认项目生成代码配置类
47 | * @param name 名称
48 | * @param selfBase 自身继承配置 - 默认 null
49 | * @param globalBase 全局继承配置 - 默认 null
50 | * @return [ISweetPropertyConfigs.ISourcesCodeGenerateConfigs]
51 | */
52 | internal fun sourcesCodeGenerateConfigs(
53 | name: String,
54 | selfBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null,
55 | globalBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null
56 | ) = object : ISweetPropertyConfigs.ISourcesCodeGenerateConfigs {
57 | override val name get() = name
58 | override val generateDirPath get() = ISweetPropertyConfigs.DEFAULT_GENERATE_DIR_PATH
59 | override val packageName get() = ""
60 | override val className get() = ""
61 | override val isEnableRestrictedAccess get() = false
62 | override val isEnable
63 | get() = selfBase?.isEnable
64 | ?: globalBase?.isEnable
65 | ?: baseGenerateConfigs(name).isEnable
66 | override val propertiesFileNames
67 | get() = selfBase?.propertiesFileNames
68 | ?: globalBase?.propertiesFileNames
69 | ?: baseGenerateConfigs(name).propertiesFileNames
70 | override val permanentKeyValues
71 | get() = selfBase?.permanentKeyValues
72 | ?: globalBase?.permanentKeyValues
73 | ?: baseGenerateConfigs(name).permanentKeyValues
74 | override val excludeKeys
75 | get() = selfBase?.excludeKeys
76 | ?: globalBase?.excludeKeys
77 | ?: baseGenerateConfigs(name).excludeKeys
78 | override val includeKeys
79 | get() = selfBase?.includeKeys
80 | ?: globalBase?.includeKeys
81 | ?: baseGenerateConfigs(name).includeKeys
82 | override val keyValuesRules
83 | get() = selfBase?.keyValuesRules
84 | ?: globalBase?.keyValuesRules
85 | ?: baseGenerateConfigs(name).keyValuesRules
86 | override val isEnableExcludeNonStringValue
87 | get() = selfBase?.isEnableExcludeNonStringValue
88 | ?: globalBase?.isEnableExcludeNonStringValue
89 | ?: baseGenerateConfigs(name).isEnableExcludeNonStringValue
90 | override val isEnableTypeAutoConversion
91 | get() = selfBase?.isEnableTypeAutoConversion
92 | ?: globalBase?.isEnableTypeAutoConversion
93 | ?: baseGenerateConfigs(name).isEnableTypeAutoConversion
94 | override val isEnableValueInterpolation
95 | get() = selfBase?.isEnableValueInterpolation
96 | ?: globalBase?.isEnableValueInterpolation
97 | ?: baseGenerateConfigs(name).isEnableValueInterpolation
98 | override val generateLocationTypes
99 | get() = selfBase?.generateLocationTypes
100 | ?: globalBase?.generateLocationTypes
101 | ?: baseGenerateConfigs(name).generateLocationTypes
102 | }
103 |
104 | /**
105 | * 获取默认构建脚本生成代码配置类
106 | * @param name 名称
107 | * @param selfBase 自身继承配置 - 默认 null
108 | * @param globalBase 全局继承配置 - 默认 null
109 | * @return [ISweetPropertyConfigs.IBuildScriptGenerateConfigs]
110 | */
111 | internal fun buildScriptGenerateConfigs(
112 | name: String,
113 | selfBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null,
114 | globalBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null
115 | ) = object : ISweetPropertyConfigs.IBuildScriptGenerateConfigs {
116 | override val name get() = name
117 | override val extensionName get() = ISweetPropertyConfigs.DEFAULT_EXTENSION_NAME
118 | override val isEnable
119 | get() = selfBase?.isEnable
120 | ?: globalBase?.isEnable
121 | ?: baseGenerateConfigs(name).isEnable
122 | override val propertiesFileNames
123 | get() = selfBase?.propertiesFileNames
124 | ?: globalBase?.propertiesFileNames
125 | ?: baseGenerateConfigs(name).propertiesFileNames
126 | override val permanentKeyValues
127 | get() = selfBase?.permanentKeyValues
128 | ?: globalBase?.permanentKeyValues
129 | ?: baseGenerateConfigs(name).permanentKeyValues
130 | override val excludeKeys
131 | get() = selfBase?.excludeKeys
132 | ?: globalBase?.excludeKeys
133 | ?: baseGenerateConfigs(name).excludeKeys
134 | override val includeKeys
135 | get() = selfBase?.includeKeys
136 | ?: globalBase?.includeKeys
137 | ?: baseGenerateConfigs(name).includeKeys
138 | override val keyValuesRules
139 | get() = selfBase?.keyValuesRules
140 | ?: globalBase?.keyValuesRules
141 | ?: baseGenerateConfigs(name).keyValuesRules
142 | override val isEnableExcludeNonStringValue
143 | get() = selfBase?.isEnableExcludeNonStringValue
144 | ?: globalBase?.isEnableExcludeNonStringValue
145 | ?: baseGenerateConfigs(name).isEnableExcludeNonStringValue
146 | override val isEnableTypeAutoConversion
147 | get() = selfBase?.isEnableTypeAutoConversion
148 | ?: globalBase?.isEnableTypeAutoConversion
149 | ?: baseGenerateConfigs(name).isEnableTypeAutoConversion
150 | override val isEnableValueInterpolation
151 | get() = selfBase?.isEnableValueInterpolation
152 | ?: globalBase?.isEnableValueInterpolation
153 | ?: baseGenerateConfigs(name).isEnableValueInterpolation
154 | override val generateLocationTypes
155 | get() = selfBase?.generateLocationTypes
156 | ?: globalBase?.generateLocationTypes
157 | ?: baseGenerateConfigs(name).generateLocationTypes
158 | }
159 |
160 | /**
161 | * 获取默认通用生成代码配置类
162 | * @param name 名称
163 | * @return [ISweetPropertyConfigs.IBaseGenerateConfigs]
164 | */
165 | private fun baseGenerateConfigs(name: String) = object : ISweetPropertyConfigs.IBaseGenerateConfigs {
166 | override val name get() = name
167 | override val isEnable get() = true
168 | override val propertiesFileNames get() = mutableListOf(ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME)
169 | override val permanentKeyValues get() = mutableMapOf()
170 | override val excludeKeys get() = mutableListOf()
171 | override val includeKeys get() = mutableListOf()
172 | override val keyValuesRules get() = mutableMapOf()
173 | override val isEnableExcludeNonStringValue get() = true
174 | override val isEnableTypeAutoConversion get() = true
175 | override val isEnableValueInterpolation get() = true
176 | override val generateLocationTypes get() = ISweetPropertyConfigs.defaultGenerateLocationTypes
177 | }
178 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/config/factory/SweetPropertyConfigsFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/28.
21 | */
22 | package com.highcapable.sweetproperty.plugin.config.factory
23 |
24 | import com.highcapable.sweetproperty.gradle.factory.fullName
25 | import com.highcapable.sweetproperty.plugin.config.default.DefaultConfigs
26 | import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
27 | import com.highcapable.sweetproperty.plugin.extension.dsl.configure.SweetPropertyConfigureExtension
28 | import com.highcapable.sweetproperty.utils.noBlank
29 | import org.gradle.api.Project
30 |
31 | /**
32 | * 获取当前配置
33 | * @param project 当前项目
34 | * @return [ISweetPropertyConfigs.ISubConfigs]
35 | */
36 | internal fun ISweetPropertyConfigs.with(project: Project) = projects[project.fullName()] ?: global
37 |
38 | /**
39 | * 创建 [ISweetPropertyConfigs.ISubConfigs] 实例
40 | * @param name 名称 - 默认 "Global"
41 | * @param global 全局配置 - 默认为自身
42 | * @return [ISweetPropertyConfigs.ISubConfigs]
43 | */
44 | internal fun SweetPropertyConfigureExtension.SubConfigureExtension.create(
45 | name: String = "Global",
46 | global: SweetPropertyConfigureExtension.SubConfigureExtension = this
47 | ) = object : ISweetPropertyConfigs.ISubConfigs {
48 | override val sourcesCode
49 | get() = this@create.sourcesCodeConfigure?.create(name, global.sourcesCodeConfigure, this@create.allConfigure, global.allConfigure)
50 | ?: global.sourcesCodeConfigure?.create(name, globalBase = this@create.allConfigure ?: global.allConfigure)
51 | ?: DefaultConfigs.subConfigs(name, base = this@create.allConfigure ?: global.allConfigure).sourcesCode
52 | override val buildScript
53 | get() = this@create.buildScriptConfigure?.create(name, global.buildScriptConfigure, this@create.allConfigure, global.allConfigure)
54 | ?: global.buildScriptConfigure?.create(name, globalBase = this@create.allConfigure ?: global.allConfigure)
55 | ?: DefaultConfigs.subConfigs(name, base = this@create.allConfigure ?: global.allConfigure).buildScript
56 | }
57 |
58 | /**
59 | * 创建 [ISweetPropertyConfigs.ISourcesCodeGenerateConfigs] 实例
60 | * @param name 名称
61 | * @param global 全局配置 - 默认 null
62 | * @param selfBase 自身继承配置 - 默认 null
63 | * @param globalBase 全局继承配置 - 默认 null
64 | * @return [ISweetPropertyConfigs.ISourcesCodeGenerateConfigs]
65 | */
66 | private fun SweetPropertyConfigureExtension.SourcesCodeGenerateConfigureExtension.create(
67 | name: String,
68 | global: SweetPropertyConfigureExtension.SourcesCodeGenerateConfigureExtension? = null,
69 | selfBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null,
70 | globalBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null
71 | ) = object : ISweetPropertyConfigs.ISourcesCodeGenerateConfigs {
72 | override val name get() = name
73 | override val generateDirPath
74 | get() = this@create.generateDirPath.noBlank()
75 | ?: global?.generateDirPath?.noBlank()
76 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).generateDirPath
77 | override val packageName
78 | get() = this@create.packageName.noBlank()
79 | ?: global?.packageName?.noBlank()
80 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).packageName
81 | override val className
82 | get() = this@create.className.noBlank()
83 | ?: global?.className?.noBlank()
84 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).className
85 | override val isEnableRestrictedAccess
86 | get() = this@create.isEnableRestrictedAccess
87 | ?: global?.isEnableRestrictedAccess
88 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnableRestrictedAccess
89 | override val isEnable
90 | get() = this@create.isEnable
91 | ?: selfBase?.isEnable
92 | ?: global?.isEnable
93 | ?: globalBase?.isEnable
94 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnable
95 | override val propertiesFileNames
96 | get() = this@create.propertiesFileNames
97 | ?: global?.propertiesFileNames
98 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).propertiesFileNames
99 | override val permanentKeyValues
100 | get() = this@create.permanentKeyValues
101 | ?: global?.permanentKeyValues
102 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).permanentKeyValues
103 | override val excludeKeys
104 | get() = this@create.excludeKeys
105 | ?: global?.excludeKeys
106 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).excludeKeys
107 | override val includeKeys
108 | get() = this@create.includeKeys
109 | ?: global?.includeKeys
110 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).includeKeys
111 | override val keyValuesRules
112 | get() = this@create.keyValuesRules
113 | ?: global?.keyValuesRules
114 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).keyValuesRules
115 | override val isEnableExcludeNonStringValue
116 | get() = this@create.isEnableExcludeNonStringValue
117 | ?: selfBase?.isEnableExcludeNonStringValue
118 | ?: global?.isEnableExcludeNonStringValue
119 | ?: globalBase?.isEnableExcludeNonStringValue
120 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnableExcludeNonStringValue
121 | override val isEnableTypeAutoConversion
122 | get() = this@create.isEnableTypeAutoConversion
123 | ?: selfBase?.isEnableTypeAutoConversion
124 | ?: global?.isEnableTypeAutoConversion
125 | ?: globalBase?.isEnableTypeAutoConversion
126 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnableTypeAutoConversion
127 | override val isEnableValueInterpolation
128 | get() = this@create.isEnableValueInterpolation
129 | ?: selfBase?.isEnableValueInterpolation
130 | ?: global?.isEnableValueInterpolation
131 | ?: globalBase?.isEnableValueInterpolation
132 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).isEnableValueInterpolation
133 | override val generateLocationTypes
134 | get() = this@create.generateLocationTypes
135 | ?: selfBase?.generateLocationTypes
136 | ?: global?.generateLocationTypes
137 | ?: globalBase?.generateLocationTypes
138 | ?: DefaultConfigs.sourcesCodeGenerateConfigs(name, selfBase, globalBase).generateLocationTypes
139 | }
140 |
141 | /**
142 | * 创建 [ISweetPropertyConfigs.IBuildScriptGenerateConfigs] 实例
143 | * @param name 名称
144 | * @param global 全局配置 - 默认 null
145 | * @param selfBase 自身继承配置 - 默认 null
146 | * @param globalBase 全局继承配置 - 默认 null
147 | * @return [ISweetPropertyConfigs.IBuildScriptGenerateConfigs]
148 | */
149 | private fun SweetPropertyConfigureExtension.BuildScriptGenerateConfigureExtension.create(
150 | name: String,
151 | global: SweetPropertyConfigureExtension.BuildScriptGenerateConfigureExtension? = null,
152 | selfBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null,
153 | globalBase: SweetPropertyConfigureExtension.BaseGenerateConfigureExtension? = null
154 | ) = object : ISweetPropertyConfigs.IBuildScriptGenerateConfigs {
155 | override val name get() = name
156 | override val extensionName
157 | get() = this@create.extensionName.noBlank()
158 | ?: global?.extensionName?.noBlank()
159 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).extensionName
160 | override val isEnable
161 | get() = this@create.isEnable
162 | ?: selfBase?.isEnable
163 | ?: global?.isEnable
164 | ?: globalBase?.isEnable
165 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).isEnable
166 | override val propertiesFileNames
167 | get() = this@create.propertiesFileNames
168 | ?: global?.propertiesFileNames
169 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).propertiesFileNames
170 | override val permanentKeyValues
171 | get() = this@create.permanentKeyValues
172 | ?: global?.permanentKeyValues
173 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).permanentKeyValues
174 | override val excludeKeys
175 | get() = this@create.excludeKeys
176 | ?: global?.excludeKeys
177 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).excludeKeys
178 | override val includeKeys
179 | get() = this@create.includeKeys
180 | ?: global?.includeKeys
181 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).includeKeys
182 | override val keyValuesRules
183 | get() = this@create.keyValuesRules
184 | ?: global?.keyValuesRules
185 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).keyValuesRules
186 | override val isEnableExcludeNonStringValue
187 | get() = this@create.isEnableExcludeNonStringValue
188 | ?: selfBase?.isEnableExcludeNonStringValue
189 | ?: global?.isEnableExcludeNonStringValue
190 | ?: globalBase?.isEnableExcludeNonStringValue
191 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).isEnableExcludeNonStringValue
192 | override val isEnableTypeAutoConversion
193 | get() = this@create.isEnableTypeAutoConversion
194 | ?: selfBase?.isEnableTypeAutoConversion
195 | ?: global?.isEnableTypeAutoConversion
196 | ?: globalBase?.isEnableTypeAutoConversion
197 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).isEnableTypeAutoConversion
198 | override val isEnableValueInterpolation
199 | get() = this@create.isEnableValueInterpolation
200 | ?: selfBase?.isEnableValueInterpolation
201 | ?: global?.isEnableValueInterpolation
202 | ?: globalBase?.isEnableValueInterpolation
203 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).isEnableValueInterpolation
204 | override val generateLocationTypes
205 | get() = this@create.generateLocationTypes
206 | ?: selfBase?.generateLocationTypes
207 | ?: global?.generateLocationTypes
208 | ?: globalBase?.generateLocationTypes
209 | ?: DefaultConfigs.buildScriptGenerateConfigs(name, selfBase, globalBase).generateLocationTypes
210 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/config/proxy/ISweetPropertyConfigs.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/25.
21 | */
22 | package com.highcapable.sweetproperty.plugin.config.proxy
23 |
24 | import com.highcapable.sweetproperty.SweetProperty
25 | import com.highcapable.sweetproperty.generated.SweetPropertyProperties
26 | import com.highcapable.sweetproperty.plugin.config.type.GenerateLocationType
27 | import com.highcapable.sweetproperty.plugin.generator.factory.PropertyValueRule
28 |
29 | /**
30 | * [SweetProperty] 配置类接口类
31 | */
32 | internal interface ISweetPropertyConfigs {
33 |
34 | companion object {
35 |
36 | /**
37 | * 默认的生成目录路径
38 | *
39 | * "build/generated/[SweetPropertyProperties.PROJECT_MODULE_NAME]"
40 | */
41 | internal const val DEFAULT_GENERATE_DIR_PATH = "build/generated/${SweetPropertyProperties.PROJECT_MODULE_NAME}"
42 |
43 | /**
44 | * 默认的属性配置文件名称
45 | *
46 | * "gradle.properties"
47 | */
48 | internal const val DEFAULT_PROPERTIES_FILE_NAME = "gradle.properties"
49 |
50 | /**
51 | * 默认的构建脚本扩展方法名称
52 | *
53 | * "property"
54 | */
55 | internal const val DEFAULT_EXTENSION_NAME = "property"
56 |
57 | /**
58 | * 默认的生成位置类型数组
59 | *
60 | * arrayOf([GenerateLocationType.CURRENT_PROJECT], [GenerateLocationType.ROOT_PROJECT])
61 | */
62 | internal val defaultGenerateLocationTypes = arrayOf(GenerateLocationType.CURRENT_PROJECT, GenerateLocationType.ROOT_PROJECT)
63 | }
64 |
65 | /** 是否启用插件 */
66 | val isEnable: Boolean
67 |
68 | /** 配置全部 */
69 | val global: ISubConfigs
70 |
71 | /** 配置每个项目数组 */
72 | val projects: MutableMap
73 |
74 | /**
75 | * 子配置类接口类
76 | */
77 | interface ISubConfigs {
78 |
79 | /** 项目生成代码配置类接口 */
80 | val sourcesCode: ISourcesCodeGenerateConfigs
81 |
82 | /** 构建脚本生成代码配置类接口 */
83 | val buildScript: IBuildScriptGenerateConfigs
84 | }
85 |
86 | /**
87 | * 项目生成代码配置类接口类
88 | */
89 | interface ISourcesCodeGenerateConfigs : IBaseGenerateConfigs {
90 |
91 | /** 自定义生成的目录路径 */
92 | val generateDirPath: String
93 |
94 | /** 自定义生成的包名 */
95 | val packageName: String
96 |
97 | /** 自定义生成的类名 */
98 | val className: String
99 |
100 | /** 是否启用受限访问功能 */
101 | val isEnableRestrictedAccess: Boolean
102 | }
103 |
104 | /**
105 | * 构建脚本生成代码配置类接口类
106 | */
107 | interface IBuildScriptGenerateConfigs : IBaseGenerateConfigs {
108 |
109 | /** 自定义构建脚本扩展方法名称 */
110 | val extensionName: String
111 | }
112 |
113 | /**
114 | * 通用生成代码配置类接口类
115 | */
116 | interface IBaseGenerateConfigs {
117 |
118 | /** 名称 */
119 | val name: String
120 |
121 | /** 是否为当前功能生成代码 */
122 | val isEnable: Boolean
123 |
124 | /** 属性配置文件名称数组 */
125 | val propertiesFileNames: MutableList
126 |
127 | /** 固定存在的属性键值数组 */
128 | val permanentKeyValues: MutableMap
129 |
130 | /** 被排除的属性键值名称数组 */
131 | val excludeKeys: MutableList
132 |
133 | /** 被包含的属性键值名称数组 */
134 | val includeKeys: MutableList
135 |
136 | /** 属性键值规则数组 */
137 | val keyValuesRules: MutableMap
138 |
139 | /** 是否启用排除非字符串类型键值内容 */
140 | val isEnableExcludeNonStringValue: Boolean
141 |
142 | /** 是否启用类型自动转换功能 */
143 | val isEnableTypeAutoConversion: Boolean
144 |
145 | /** 是否启用键值内容插值功能 */
146 | val isEnableValueInterpolation: Boolean
147 |
148 | /** 生成的位置类型数组 */
149 | val generateLocationTypes: Array
150 | }
151 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/config/type/GenerateLocationType.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/25.
21 | */
22 | @file:Suppress("unused")
23 |
24 | package com.highcapable.sweetproperty.plugin.config.type
25 |
26 | /**
27 | * 生成位置类型定义类
28 | */
29 | internal enum class GenerateLocationType {
30 | /** 当前项目 */
31 | CURRENT_PROJECT,
32 |
33 | /** 根项目 */
34 | ROOT_PROJECT,
35 |
36 | /** 全局 */
37 | GLOBAL,
38 |
39 | /** 系统 */
40 | SYSTEM,
41 |
42 | /** 系统环境变量 */
43 | SYSTEM_ENV
44 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/extension/accessors/proxy/IExtensionAccessors.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.plugin.extension.accessors.proxy
23 |
24 | /**
25 | * 扩展可访问 [Class] 定义空间接口
26 | */
27 | internal interface IExtensionAccessors
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/extension/dsl/configure/SweetPropertyConfigureExtension.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/25.
21 | */
22 | @file:Suppress("unused", "MemberVisibilityCanBePrivate", "PropertyName", "DeprecatedCallableAddReplaceWith")
23 |
24 | package com.highcapable.sweetproperty.plugin.extension.dsl.configure
25 |
26 | import com.highcapable.sweetproperty.SweetProperty
27 | import com.highcapable.sweetproperty.gradle.factory.isUnSafeExtName
28 | import com.highcapable.sweetproperty.plugin.config.factory.create
29 | import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
30 | import com.highcapable.sweetproperty.plugin.config.type.GenerateLocationType
31 | import com.highcapable.sweetproperty.plugin.generator.factory.PropertyValueRule
32 | import com.highcapable.sweetproperty.utils.debug.SError
33 | import com.highcapable.sweetproperty.utils.noEmpty
34 | import org.gradle.api.Action
35 | import org.gradle.api.initialization.Settings
36 |
37 | /**
38 | * [SweetProperty] 配置方法体实现类
39 | */
40 | open class SweetPropertyConfigureExtension internal constructor() {
41 |
42 | internal companion object {
43 |
44 | /** [SweetPropertyConfigureExtension] 扩展名称 */
45 | internal const val NAME = "sweetProperty"
46 |
47 | /** 根项目标识名称 */
48 | private const val ROOT_PROJECT_TAG = ""
49 | }
50 |
51 | /** 当前全局配置实例 */
52 | internal val globalConfigure = SubConfigureExtension()
53 |
54 | /** 当前每个项目配置实例数组 */
55 | internal val projectConfigures = mutableMapOf()
56 |
57 | /** 当前项目 */
58 | @JvmField
59 | val CURRENT_PROJECT = "CURRENT_PROJECT"
60 |
61 | /** 根项目 */
62 | @JvmField
63 | val ROOT_PROJECT = "ROOT_PROJECT"
64 |
65 | /** 全局 (用户目录) */
66 | @JvmField
67 | val GLOBAL = "GLOBAL"
68 |
69 | /** 系统 */
70 | @JvmField
71 | val SYSTEM = "SYSTEM"
72 |
73 | /** 系统环境变量 */
74 | @JvmField
75 | val SYSTEM_ENV = "SYSTEM_ENV"
76 |
77 | /**
78 | * 是否启用插件
79 | *
80 | * 默认启用 - 如果你想关闭插件 - 在这里设置就可以了
81 | */
82 | var isEnable = true
83 | @JvmName("enable") set
84 |
85 | /**
86 | * 配置全局
87 | * @param action 配置方法体
88 | */
89 | fun global(action: Action) = action.execute(globalConfigure)
90 |
91 | /**
92 | * 配置根项目
93 | * @param action 配置方法体
94 | */
95 | fun rootProject(action: Action) = configureProject(ROOT_PROJECT_TAG, action)
96 |
97 | /**
98 | * 配置指定项目 (数组)
99 | * @param names 项目完整名称 (数组)
100 | * @param action 配置方法体
101 | */
102 | fun project(vararg names: String, action: Action) = names.forEach { configureProject(it, action) }
103 |
104 | /**
105 | * 配置项目
106 | * @param name 项目完整名称
107 | * @param action 配置方法体
108 | */
109 | private fun configureProject(name: String, action: Action) =
110 | action.execute(SubConfigureExtension().also { projectConfigures[name] = it })
111 |
112 | /**
113 | * 子配置方法体实现类
114 | */
115 | open inner class SubConfigureExtension internal constructor() {
116 |
117 | /** 当前通用生成代码功能配置实例 */
118 | internal var allConfigure: BaseGenerateConfigureExtension? = null
119 |
120 | /** 当前项目生成代码功能配置实例 */
121 | internal var sourcesCodeConfigure: SourcesCodeGenerateConfigureExtension? = null
122 |
123 | /** 当前构建脚本生成代码功能配置实例 */
124 | internal var buildScriptConfigure: BuildScriptGenerateConfigureExtension? = null
125 |
126 | /**
127 | * 错误的调用会导致关闭整个插件的功能
128 | *
129 | * 请使用 [all]、[sourcesCode]、[buildScript]
130 | * @throws [IllegalStateException]
131 | */
132 | @Deprecated(message = "Do not use", level = DeprecationLevel.ERROR)
133 | val isEnable: Boolean
134 | get() = SError.make("Please called all { isEnable = ... }, sourcesCode { isEnable = ... }, buildScript { isEnable = ... }")
135 |
136 | /**
137 | * 配置通用生成代码功能
138 | * @param action 配置方法体
139 | */
140 | fun all(action: Action) {
141 | allConfigure = BaseGenerateConfigureExtension().also { action.execute(it) }
142 | }
143 |
144 | /**
145 | * 配置项目生成代码功能
146 | * @param action 配置方法体
147 | */
148 | fun sourcesCode(action: Action) {
149 | sourcesCodeConfigure = SourcesCodeGenerateConfigureExtension().also { action.execute(it) }
150 | }
151 |
152 | /**
153 | * 配置构建脚本生成代码功能
154 | * @param action 配置方法体
155 | */
156 | fun buildScript(action: Action) {
157 | buildScriptConfigure = BuildScriptGenerateConfigureExtension().also { action.execute(it) }
158 | }
159 | }
160 |
161 | /**
162 | * 项目生成代码配置方法体实现类
163 | */
164 | open inner class SourcesCodeGenerateConfigureExtension internal constructor() : BaseGenerateConfigureExtension() {
165 |
166 | /**
167 | * 自定义生成的目录路径
168 | *
169 | * 你可以填写相对于当前项目的路径
170 | *
171 | * 默认为 [ISweetPropertyConfigs.DEFAULT_GENERATE_DIR_PATH]
172 | */
173 | var generateDirPath = ""
174 | @JvmName("generateDirPath") set
175 |
176 | /**
177 | * 自定义生成的包名
178 | *
179 | * Android 项目默认使用 "android" 配置方法块中的 "namespace"
180 | *
181 | * 普通的 Kotlin on Jvm 项目默认使用项目设置的 "project.group"
182 | */
183 | var packageName = ""
184 | @JvmName("packageName") set
185 |
186 | /**
187 | * 自定义生成的类名
188 | *
189 | * 默认使用当前项目的名称 + "Properties"
190 | */
191 | var className = ""
192 | @JvmName("className") set
193 |
194 | /**
195 | * 是否启用受限访问功能
196 | *
197 | * 默认不启用 - 启用后将为生成的类和方法添加 "internal" 修饰符
198 | */
199 | var isEnableRestrictedAccess: Boolean? = null
200 | @JvmName("enableRestrictedAccess") set
201 | }
202 |
203 | /**
204 | * 构建脚本生成代码配置方法体实现类
205 | */
206 | open inner class BuildScriptGenerateConfigureExtension internal constructor() : BaseGenerateConfigureExtension() {
207 |
208 | /**
209 | * 自定义构建脚本扩展方法名称
210 | *
211 | * 默认为 [ISweetPropertyConfigs.DEFAULT_EXTENSION_NAME]
212 | */
213 | var extensionName = ""
214 | @JvmName("extensionName") set
215 | }
216 |
217 | /**
218 | * 通用生成代码配置方法体实现类
219 | */
220 | open inner class BaseGenerateConfigureExtension internal constructor() {
221 |
222 | /** 当前属性配置文件路径数组 */
223 | internal var propertiesFileNames: MutableList? = null
224 |
225 | /** 当前固定存在的属性键值数组 */
226 | internal var permanentKeyValues: MutableMap? = null
227 |
228 | /** 当前被排除的属性键值名称数组 */
229 | internal var excludeKeys: MutableList? = null
230 |
231 | /** 当前被包含的属性键值名称数组 */
232 | internal var includeKeys: MutableList? = null
233 |
234 | /** 当前属性键值规则数组 */
235 | internal var keyValuesRules: MutableMap? = null
236 |
237 | /** 当前生成位置类型数组 */
238 | internal var generateLocationTypes: Array? = null
239 |
240 | /**
241 | * 是否为当前功能生成代码
242 | *
243 | * 默认启用
244 | *
245 | * [SourcesCodeGenerateConfigureExtension] 启用后将会为当前项目生成代码并添加到当前项目的 sourceSets 中
246 | *
247 | * [BuildScriptGenerateConfigureExtension] 启用后将会为构建脚本生成代码并为构建脚本生成扩展方法
248 | *
249 | * 在 [BuildScriptGenerateConfigureExtension] 中你可以使用 [BuildScriptGenerateConfigureExtension.extensionName] 来自定义构建脚本扩展方法名称
250 | */
251 | var isEnable: Boolean? = null
252 | @JvmName("enable") set
253 |
254 | /**
255 | * 设置属性配置文件名称
256 | *
257 | * - 此方法已弃用 - 在之后的版本中将直接被删除
258 | *
259 | * - 请现在迁移到 [propertiesFileNames]
260 | */
261 | @Deprecated(message = "Migrate to propertiesFileNames(...)")
262 | var propertiesFileName = ""
263 | @JvmName("propertiesFileName") set
264 |
265 | /**
266 | * 是否启用排除非字符串类型键值内容
267 | *
268 | * 默认启用 - 启用后将从属性键值中排除不是字符串类型的键值及内容
269 | */
270 | var isEnableExcludeNonStringValue: Boolean? = null
271 | @JvmName("enableExcludeNonStringValue") set
272 |
273 | /**
274 | * 是否启用类型自动转换功能
275 | *
276 | * 默认启用 - 启用后将自动识别属性键值中的类型并转换为对应的类型
277 | *
278 | * 在启用后如果你想要强制设置一个键值内容为字符串类型 - 你可以使用单引号或双引号包裹整个字符串
279 | *
280 | * 注意:在关闭此功能后如上所述的功能也将同时失效
281 | */
282 | var isEnableTypeAutoConversion: Boolean? = null
283 | @JvmName("enableTypeAutoConversion") set
284 |
285 | /**
286 | * 是否启用键值内容插值功能
287 | *
288 | * 默认启用 - 启用后将自动识别属性键值内容中的 ${...} 内容并进行替换
289 | *
290 | * 注意:插值的内容仅会从当前 (当前配置文件) 属性键值列表进行查找
291 | */
292 | var isEnableValueInterpolation: Boolean? = null
293 | @JvmName("enableValueInterpolation") set
294 |
295 | /**
296 | * 设置属性配置文件名称数组
297 | *
298 | * 属性配置文件将根据你设置的文件名称自动从当前根项目、子项目以及用户目录的根目录进行获取
299 | *
300 | * 默认为 [ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME]
301 | *
302 | * 你可以添加多组属性配置文件名称 - 将按照顺序依次进行读取
303 | *
304 | * - 注意:一般情况下不需要修改此设置 - 错误的文件名称将导致获取到空键值内容
305 | * @param names 要添加的属性配置文件名称数组
306 | * @param isAddDefault 是否添加默认的属性配置文件名称 - 默认是
307 | */
308 | @JvmOverloads
309 | fun propertiesFileNames(vararg names: String, isAddDefault: Boolean = true) {
310 | if (names.isEmpty()) SError.make("Properties file names must not be empty")
311 | if (names.any { it.isBlank() }) SError.make("Properties file names must not have blank contents")
312 | propertiesFileNames = names.distinct().toMutableList()
313 | if (isAddDefault) propertiesFileNames?.add(0, ISweetPropertyConfigs.DEFAULT_PROPERTIES_FILE_NAME)
314 | }
315 |
316 | /**
317 | * 设置固定存在的属性键值数组
318 | *
319 | * 在这里可以设置一些一定存在的键值 - 这些键值无论能否从属性键值中得到都会进行生成
320 | *
321 | * 这些键值在属性键值存在时使用属性键值的内容 - 不存在时使用这里设置的内容
322 | *
323 | * - 注意:属性键值名称不能存在特殊符号以及空格 - 否则可能会生成失败
324 | * @param pairs 键值数组
325 | */
326 | @JvmName("-kotlin-dsl-only-permanentKeyValues-")
327 | fun permanentKeyValues(vararg pairs: Pair) {
328 | if (pairs.isEmpty()) SError.make("Permanent key-values must not be empty")
329 | if (pairs.any { it.first.isBlank() }) SError.make("Permanent key-values must not have blank contents")
330 | permanentKeyValues = mutableMapOf(*pairs)
331 | }
332 |
333 | /**
334 | * 设置固定存在的属性键值数组 (Groovy 兼容方法)
335 | *
336 | * 在这里可以设置一些一定存在的键值 - 这些键值无论能否从属性键值中得到都会进行生成
337 | *
338 | * 这些键值在属性键值存在时使用属性键值的内容 - 不存在时使用这里设置的内容
339 | *
340 | * - 注意:属性键值名称不能存在特殊符号以及空格 - 否则可能会生成失败
341 | * @param keyValues 键值数组
342 | */
343 | fun permanentKeyValues(keyValues: Map) {
344 | if (keyValues.isEmpty()) SError.make("Permanent key-values must not be empty")
345 | if (keyValues.any { it.key.isBlank() }) SError.make("Permanent key-values must not have blank contents")
346 | permanentKeyValues = keyValues.toMutableMap()
347 | }
348 |
349 | /**
350 | * 设置需要排除的属性键值名称数组
351 | *
352 | * 在这里可以设置一些你希望从已知的属性键值中排除的键值名称
353 | *
354 | * 这些键值在属性键值存在它们时被排除 - 不会出现在生成的代码中
355 | *
356 | * - 注意:如果你排除了 [permanentKeyValues] 中设置的键值 -
357 | * 那么它们只会变为你设置的初始键值内容并继续保持存在
358 | * @param keys 键值名称数组 - 你可以传入 [Regex] 或使用 [String.toRegex] 以使用正则功能
359 | */
360 | fun excludeKeys(vararg keys: Any) {
361 | if (keys.isEmpty()) SError.make("Exclude keys must not be empty")
362 | if (keys.any { it.toString().isBlank() }) SError.make("Exclude keys must not have blank contents")
363 | excludeKeys = keys.distinct().toMutableList()
364 | }
365 |
366 | /**
367 | * 设置需要包含的属性键值名称数组
368 | *
369 | * 在这里可以设置一些你希望从已知的属性键值中包含的键值名称
370 | *
371 | * 这些键值在属性键值存在它们时被包含 - 未被包含的键值不会出现在生成的代码中
372 | * @param keys 键值名称数组 - 你可以传入 [Regex] 或使用 [String.toRegex] 以使用正则功能
373 | */
374 | fun includeKeys(vararg keys: Any) {
375 | if (keys.isEmpty()) SError.make("Include keys must not be empty")
376 | if (keys.any { it.toString().isBlank() }) SError.make("Include keys must not have blank contents")
377 | includeKeys = keys.distinct().toMutableList()
378 | }
379 |
380 | /**
381 | * 设置属性键值规则数组
382 | *
383 | * 你可以设置一组键值规则 - 使用 [createValueRule] 创建新的规则 - 用于解析得到的键值内容
384 | *
385 | * 示例如下 ↓
386 | *
387 | * ```kotlin
388 | * keyValuesRules(
389 | * "some.key1" to createValueRule { if (it.contains("_")) it.replace("_", "-") else it },
390 | * "some.key2" to createValueRule { "$it-value" }
391 | * )
392 | * ```
393 | *
394 | * 这些键值规则在属性键值存在它们时被应用
395 | * @param pairs 属性键值规则数组
396 | */
397 | @JvmName("-kotlin-dsl-only-keyValuesRules-")
398 | fun keyValuesRules(vararg pairs: Pair) {
399 | if (pairs.isEmpty()) SError.make("Key-values rules must not be empty")
400 | if (pairs.any { it.first.isBlank() }) SError.make("Key-values rules must not have blank contents")
401 | keyValuesRules = mutableMapOf(*pairs)
402 | }
403 |
404 | /**
405 | * 设置属性键值规则数组 (Groovy 兼容方法)
406 | *
407 | * 你可以设置一组键值规则 - 使用 [createValueRule] 创建新的规则 - 用于解析得到的键值内容
408 | *
409 | * 这些键值规则在属性键值存在它们时被应用
410 | * @param rules 属性键值规则数组
411 | */
412 | fun keyValuesRules(rules: Map) {
413 | if (rules.isEmpty()) SError.make("Key-values rules must not be empty")
414 | if (rules.any { it.key.isBlank() }) SError.make("Key-values rules must not have blank contents")
415 | keyValuesRules = rules.toMutableMap()
416 | }
417 |
418 | /**
419 | * 创建新的属性键值规则
420 | * @param rule 回调当前规则
421 | * @return [PropertyValueRule]
422 | */
423 | fun createValueRule(rule: PropertyValueRule) = rule
424 |
425 | /**
426 | * 设置从何处生成属性键值
427 | *
428 | * 默认为 [ISweetPropertyConfigs.defaultGenerateLocationTypes]
429 | *
430 | * 你可以使用以下类型来进行设置
431 | *
432 | * - [CURRENT_PROJECT]
433 | * - [ROOT_PROJECT]
434 | * - [GLOBAL]
435 | * - [SYSTEM]
436 | * - [SYSTEM_ENV]
437 | *
438 | * [SweetProperty] 将从你设置的生成位置依次生成属性键值 - 生成位置的顺序跟随你设置的顺序决定
439 | *
440 | * - 风险提示:[GLOBAL]、[SYSTEM]、[SYSTEM_ENV] 可能存在密钥和证书 - 请小心管理生成的代码
441 | * @param types 位置类型数组
442 | */
443 | fun generateFrom(vararg types: String) {
444 | generateLocationTypes = mutableListOf().apply {
445 | types.toList().noEmpty()?.forEach {
446 | add(when (it) {
447 | CURRENT_PROJECT -> GenerateLocationType.CURRENT_PROJECT
448 | ROOT_PROJECT -> GenerateLocationType.ROOT_PROJECT
449 | GLOBAL -> GenerateLocationType.GLOBAL
450 | SYSTEM -> GenerateLocationType.SYSTEM
451 | SYSTEM_ENV -> GenerateLocationType.SYSTEM_ENV
452 | else -> SError.make("Invalid generate location type \"$it\"")
453 | })
454 | } ?: SError.make("Generate location types must not be empty")
455 | }.toTypedArray()
456 | }
457 | }
458 |
459 | /**
460 | * 构造 [ISweetPropertyConfigs]
461 | * @param settings 当前设置
462 | * @return [ISweetPropertyConfigs]
463 | */
464 | internal fun build(settings: Settings): ISweetPropertyConfigs {
465 | /**
466 | * 检查是否以字母开头
467 | * @param description 描述
468 | */
469 | fun String.checkingStartWithLetter(description: String) {
470 | firstOrNull()?.also {
471 | if (it !in 'A'..'Z' && it !in 'a'..'z') SError.make("$description name \"$this\" must start with a letter")
472 | }
473 | }
474 |
475 | /** 检查合法包名 */
476 | fun String.checkingValidPackageName() {
477 | if (isNotBlank() && !matches("^[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*$".toRegex()))
478 | SError.make("Invalid package name \"$this\"")
479 | }
480 |
481 | /** 检查合法类名 */
482 | fun String.checkingValidClassName() {
483 | if (isNotBlank() && !matches("^[a-zA-Z][a-zA-Z0-9_]*$".toRegex()))
484 | SError.make("Invalid class name \"$this\"")
485 | }
486 |
487 | /** 检查合法扩展方法名称 */
488 | fun String.checkingValidExtensionName() {
489 | checkingStartWithLetter(description = "Extension")
490 | if (isNotBlank() && isUnSafeExtName()) SError.make("This name \"$this\" is a Gradle built-in extension")
491 | }
492 |
493 | /** 检查名称是否合法 */
494 | fun SweetPropertyConfigureExtension.SubConfigureExtension.checkingNames() {
495 | sourcesCodeConfigure?.packageName?.checkingValidPackageName()
496 | sourcesCodeConfigure?.className?.checkingValidClassName()
497 | buildScriptConfigure?.extensionName?.checkingValidExtensionName()
498 | }
499 | val currentEnable = isEnable
500 | globalConfigure.checkingNames()
501 | val currentGlobal = globalConfigure.create()
502 | val currentProjects = mutableMapOf()
503 | val rootName = settings.rootProject.name
504 | if (projectConfigures.any { (name, _) -> name.lowercase() == rootName.lowercase() })
505 | SError.make("This name \"$rootName\" is a root project, please use rootProject function to configure it, not project(\"$rootName\")")
506 | if (projectConfigures.containsKey(ROOT_PROJECT_TAG)) {
507 | projectConfigures[rootName] = projectConfigures[ROOT_PROJECT_TAG] ?: SError.make("Internal error")
508 | projectConfigures.remove(ROOT_PROJECT_TAG)
509 | }
510 | projectConfigures.forEach { (name, subConfigure) ->
511 | name.replaceFirst(":", "").checkingStartWithLetter(description = "Project")
512 | subConfigure.checkingNames()
513 | currentProjects[name] = subConfigure.create(name, globalConfigure)
514 | }; return object : ISweetPropertyConfigs {
515 | override val isEnable get() = currentEnable
516 | override val global get() = currentGlobal
517 | override val projects get() = currentProjects
518 | }
519 | }
520 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/generator/PropertiesAccessorsGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.plugin.generator
23 |
24 | import com.highcapable.sweetproperty.SweetProperty
25 | import com.highcapable.sweetproperty.generated.SweetPropertyProperties
26 | import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
27 | import com.highcapable.sweetproperty.plugin.extension.accessors.proxy.IExtensionAccessors
28 | import com.highcapable.sweetproperty.plugin.generator.factory.PropertyMap
29 | import com.highcapable.sweetproperty.plugin.generator.factory.parseTypedValue
30 | import com.highcapable.sweetproperty.plugin.generator.factory.toOptimize
31 | import com.highcapable.sweetproperty.utils.capitalize
32 | import com.highcapable.sweetproperty.utils.debug.SError
33 | import com.highcapable.sweetproperty.utils.firstNumberToLetter
34 | import com.highcapable.sweetproperty.utils.toNonJavaName
35 | import com.highcapable.sweetproperty.utils.uncapitalize
36 | import com.highcapable.sweetproperty.utils.uppercamelcase
37 | import com.squareup.javapoet.ClassName
38 | import com.squareup.javapoet.FieldSpec
39 | import com.squareup.javapoet.JavaFile
40 | import com.squareup.javapoet.MethodSpec
41 | import com.squareup.javapoet.TypeSpec
42 | import java.text.SimpleDateFormat
43 | import java.util.*
44 | import javax.annotation.Nonnull
45 | import javax.lang.model.element.Modifier
46 | import kotlin.properties.Delegates
47 |
48 | /**
49 | * 属性键值可访问 [Class] 生成实现类
50 | */
51 | internal class PropertiesAccessorsGenerator {
52 |
53 | private companion object {
54 |
55 | /** 生成的 [Class] 所在包名 */
56 | private const val ACCESSORS_PACKAGE_NAME = "${SweetPropertyProperties.PROJECT_GROUP_NAME}.plugin.extension.accessors.generated"
57 |
58 | /** 生成的 [Class] 后缀名 */
59 | private const val CLASS_SUFFIX_NAME = "Accessors"
60 |
61 | /** 生成的首位 [Class] 后缀名 */
62 | private const val TOP_CLASS_SUFFIX_NAME = "Properties$CLASS_SUFFIX_NAME"
63 |
64 | /** 标识首位生成的 [Class] TAG */
65 | private const val TOP_SUCCESSIVE_NAME = "_top_successive_name"
66 | }
67 |
68 | /** 当前配置实例 */
69 | private var configs by Delegates.notNull()
70 |
71 | /** 生成的属性键值 [Class] 构建器数组 */
72 | private val classSpecs = mutableMapOf()
73 |
74 | /** 生成的属性键值构造方法构建器数组 */
75 | private val constructorSpecs = mutableMapOf()
76 |
77 | /** 生成的属性键值预添加的构造方法名称数组 */
78 | private val preAddConstructorSpecNames = mutableListOf>()
79 |
80 | /** 生成的属性键值 [Class] 扩展类名数组 */
81 | private val memoryExtensionClasses = mutableMapOf()
82 |
83 | /** 生成的属性键值连续名称记录数组 */
84 | private val grandSuccessiveNames = mutableListOf()
85 |
86 | /** 生成的属性键值连续名称重复次数数组 */
87 | private val grandSuccessiveDuplicateIndexs = mutableMapOf()
88 |
89 | /** 生成的属性键值不重复调用方法数组 */
90 | private val usedSuccessiveMethods = mutableMapOf>()
91 |
92 | /** 生成的属性键值不重复 TAG 数组 */
93 | private val usedSuccessiveTags = mutableSetOf()
94 |
95 | /**
96 | * 不重复调用
97 | * @param tags 当前 TAG 数组
98 | * @param block 执行的方法块
99 | */
100 | private inline fun noRepeated(vararg tags: String, block: () -> Unit) {
101 | val allTag = tags.joinToString("-")
102 | if (!usedSuccessiveTags.contains(allTag)) block()
103 | usedSuccessiveTags.add(allTag)
104 | }
105 |
106 | /**
107 | * 字符串首字母大写并添加 [CLASS_SUFFIX_NAME] 后缀
108 | * @return [String]
109 | */
110 | private fun String.capitalized() = "${capitalize()}$CLASS_SUFFIX_NAME"
111 |
112 | /**
113 | * 字符串首字母小写并添加 [CLASS_SUFFIX_NAME] 后缀
114 | * @return [String]
115 | */
116 | private fun String.uncapitalized() = "${uncapitalize()}$CLASS_SUFFIX_NAME"
117 |
118 | /**
119 | * 字符串类名转换为 [ClassName]
120 | * @param packageName 包名 - 默认空
121 | * @return [ClassName]
122 | */
123 | private fun String.asClassType(packageName: String = "") = ClassName.get(packageName, this)
124 |
125 | /**
126 | * 通过 [TypeSpec] 创建 [JavaFile]
127 | * @return [JavaFile]
128 | */
129 | private fun TypeSpec.createJavaFile(packageName: String) = JavaFile.builder(packageName, this).build()
130 |
131 | /**
132 | * 创建通用构建器描述类
133 | * @param name 名称
134 | * @param accessorsName 接续名 - 默认空
135 | * @param isInner 是否为内部类 - 默认是
136 | * @return [TypeSpec.Builder]
137 | */
138 | private fun createClassSpec(name: String, accessorsName: String = "", isInner: Boolean = true) =
139 | TypeSpec.classBuilder(if (isInner) name.capitalized() else name).apply {
140 | if (isInner) {
141 | addJavadoc("The \"$accessorsName\" accessors")
142 | addSuperinterface(IExtensionAccessors::class.java)
143 | addModifiers(Modifier.PUBLIC, Modifier.STATIC)
144 | } else {
145 | addJavadoc(
146 | """
147 | This class is generated by ${SweetProperty.TAG} at ${SimpleDateFormat.getDateTimeInstance().format(Date())}
148 |
149 | The content here is automatically generated according to the properties of your projects
150 |
151 | You can visit here for more help
152 | """.trimIndent()
153 | )
154 | addModifiers(Modifier.PUBLIC)
155 | }
156 | }
157 |
158 | /**
159 | * 创建通用构造方法构建器描述类
160 | * @return [MethodSpec.Builder]
161 | */
162 | private fun createConstructorSpec() = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC)
163 |
164 | /**
165 | * 向通用构建器描述类添加变量
166 | * @param accessorsName 接续名
167 | * @param className 类名
168 | * @return [TypeSpec.Builder]
169 | */
170 | private fun TypeSpec.Builder.addSuccessiveField(accessorsName: String, className: String) = addField(
171 | FieldSpec.builder(className.capitalized().asClassType(), className.uncapitalized(), Modifier.PRIVATE, Modifier.FINAL)
172 | .addJavadoc("Create the \"$accessorsName\" accessors")
173 | .build()
174 | )
175 |
176 | /**
177 | * 向通用构建器描述类添加方法
178 | * @param accessorsName 接续名
179 | * @param methodName 方法名
180 | * @param className 类名
181 | * @return [TypeSpec.Builder]
182 | */
183 | private fun TypeSpec.Builder.addSuccessiveMethod(accessorsName: String, methodName: String, className: String) =
184 | addMethod(
185 | MethodSpec.methodBuilder("get${getOrCreateUsedSuccessiveMethodName(methodName, className).capitalize()}")
186 | .addJavadoc("Resolve the \"$accessorsName\" accessors")
187 | .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
188 | .addAnnotation(Nonnull::class.java)
189 | .returns(className.capitalized().asClassType())
190 | .addStatement("return ${className.uncapitalized()}")
191 | .build()
192 | )
193 |
194 | /**
195 | * 向通用构建器描述类添加最终键值方法
196 | * @param accessorsName 接续名
197 | * @param methodName 方法名
198 | * @param className 类名
199 | * @param value 键值内容
200 | * @return [TypeSpec.Builder]
201 | */
202 | private fun TypeSpec.Builder.addFinalValueMethod(accessorsName: String, methodName: String, className: String, value: Any) =
203 | addMethod(
204 | MethodSpec.methodBuilder("get${getOrCreateUsedSuccessiveMethodName(methodName, className).capitalize()}").apply {
205 | val typedValue = value.parseTypedValue(configs.isEnableTypeAutoConversion)
206 | addJavadoc("Resolve the \"$accessorsName\" value ${typedValue.second}")
207 | addModifiers(Modifier.PUBLIC, Modifier.FINAL)
208 | .addAnnotation(Nonnull::class.java)
209 | .returns(typedValue.first.java)
210 | .addStatement("return ${typedValue.second}")
211 | }.build()
212 | )
213 |
214 | /**
215 | * 向通用构造方法构建器描述类添加变量实例化语句
216 | * @param className 类名
217 | * @return [MethodSpec.Builder]
218 | */
219 | private fun MethodSpec.Builder.addSuccessiveStatement(className: String) =
220 | addStatement("${className.uncapitalized()} = new ${className.capitalized()}()")
221 |
222 | /**
223 | * 获取不重复调用方法名称
224 | * @param methodName 方法名
225 | * @param className 类名
226 | * @return [String]
227 | */
228 | private fun getOrCreateUsedSuccessiveMethodName(methodName: String, className: String): String {
229 | if (usedSuccessiveMethods[className] == null) usedSuccessiveMethods[className] = mutableListOf()
230 | val methods = usedSuccessiveMethods[className]!!
231 | val finalName = if (methods.contains(methodName)) "$methodName${methods.filter { it == methodName }.size + 1}" else methodName
232 | methods.add(methodName)
233 | return finalName
234 | }
235 |
236 | /**
237 | * 获取、创建通用构建器描述类
238 | * @param name 名称
239 | * @param accessorsName 接续名 - 默认空
240 | * @return [TypeSpec.Builder]
241 | */
242 | private fun getOrCreateClassSpec(name: String, accessorsName: String = "") =
243 | classSpecs[name] ?: createClassSpec(name, accessorsName).also { classSpecs[name] = it }
244 |
245 | /**
246 | * 获取、创建通用构造方法构建器描述类
247 | * @param name 名称
248 | * @return [MethodSpec.Builder]
249 | */
250 | private fun getOrCreateConstructorSpec(name: String) = constructorSpecs[name] ?: createConstructorSpec().also { constructorSpecs[name] = it }
251 |
252 | /**
253 | * 解析并生成所有类的构建器 (核心方法)
254 | *
255 | * 解析开始前需要确保已调用 [createTopClassSpec] 并调用一次 [clearGeneratedData] 防止数据混淆
256 | *
257 | * 解析完成后需要调用 [releaseParseTypeSpec] 完成解析
258 | * @param successiveName 连续的名称
259 | * @param key 键值名称 (原始名称)
260 | * @param value 键值内容
261 | */
262 | private fun parseTypeSpec(successiveName: String, key: String, value: Any) {
263 | /**
264 | * 获取生成的属性键值连续名称重复次数
265 | * @return [Int]
266 | */
267 | fun String.duplicateGrandSuccessiveIndex() = lowercase().let { name ->
268 | if (grandSuccessiveDuplicateIndexs.contains(name)) {
269 | grandSuccessiveDuplicateIndexs[name] = (grandSuccessiveDuplicateIndexs[name] ?: 1) + 1
270 | grandSuccessiveDuplicateIndexs[name] ?: 2
271 | } else 2.also { grandSuccessiveDuplicateIndexs[name] = it }
272 | }
273 |
274 | /**
275 | * 解析 (拆分) 名称到数组
276 | *
277 | * 形如 "com.mytest" → "ComMytest" → "mytest"
278 | * @return [List]<[Triple]<[String], [String], [String]>>
279 | */
280 | fun String.parseSuccessiveNames(): List> {
281 | var grandAcccessorsName = ""
282 | var grandSuccessiveName = ""
283 | val successiveNames = mutableListOf>()
284 | val splitNames = split("_").dropWhile { it.isBlank() }.ifEmpty { listOf(this) }
285 | splitNames.forEach { eachName ->
286 | val name = eachName.capitalize().toNonJavaName().firstNumberToLetter()
287 | grandAcccessorsName += if (grandAcccessorsName.isNotBlank()) ".$eachName" else eachName
288 | grandSuccessiveName += name
289 | if (grandSuccessiveNames.any { it != grandSuccessiveName && it.lowercase() == grandSuccessiveName.lowercase() })
290 | grandSuccessiveName += duplicateGrandSuccessiveIndex().toString()
291 | grandSuccessiveNames.add(grandSuccessiveName)
292 | successiveNames.add(Triple(grandAcccessorsName, grandSuccessiveName, name))
293 | }; return successiveNames.distinct()
294 | }
295 | val successiveNames = successiveName.parseSuccessiveNames()
296 | successiveNames.forEachIndexed { index, (accessorsName, className, methodName) ->
297 | val nextItem = successiveNames.getOrNull(index + 1)
298 | val lastItem = successiveNames.getOrNull(successiveNames.lastIndex)
299 | val nextAccessorsName = nextItem?.first ?: ""
300 | val nextClassName = nextItem?.second ?: ""
301 | val nextMethodName = nextItem?.third ?: ""
302 | val lastClassName = lastItem?.second ?: ""
303 | val lastMethodName = lastItem?.third ?: ""
304 | val isPreLastIndex = index == successiveNames.lastIndex - 1
305 | if (successiveNames.size == 1) getOrCreateClassSpec(TOP_SUCCESSIVE_NAME).addFinalValueMethod(key, methodName, className, value)
306 | if (index == successiveNames.lastIndex) return@forEachIndexed
307 | if (index == 0) noRepeated(TOP_SUCCESSIVE_NAME, methodName, className) {
308 | getOrCreateClassSpec(TOP_SUCCESSIVE_NAME, accessorsName)
309 | .addSuccessiveField(accessorsName, className)
310 | .addSuccessiveMethod(accessorsName, methodName, className)
311 | getOrCreateConstructorSpec(TOP_SUCCESSIVE_NAME).addSuccessiveStatement(className)
312 | }
313 | noRepeated(className, nextMethodName, nextClassName) {
314 | getOrCreateClassSpec(className, accessorsName).apply {
315 | if (!isPreLastIndex) {
316 | addSuccessiveField(nextAccessorsName, nextClassName)
317 | addSuccessiveMethod(nextAccessorsName, nextMethodName, nextClassName)
318 | } else addFinalValueMethod(key, lastMethodName, lastClassName, value)
319 | }
320 | if (!isPreLastIndex) preAddConstructorSpecNames.add(className to nextClassName)
321 | }
322 | }
323 | }
324 |
325 | /** 完成生成所有类的构建器 (释放) */
326 | private fun releaseParseTypeSpec() =
327 | preAddConstructorSpecNames.onEach { (topClassName, innerClassName) ->
328 | getOrCreateConstructorSpec(topClassName)?.addSuccessiveStatement(innerClassName)
329 | }.clear()
330 |
331 | /**
332 | * 解析并生成所有类的构建器
333 | * @return [TypeSpec]
334 | */
335 | private fun buildTypeSpec(): TypeSpec {
336 | classSpecs.forEach { (name, typeSpec) ->
337 | constructorSpecs[name]?.build()?.let { typeSpec.addMethod(it) }
338 | if (name != TOP_SUCCESSIVE_NAME) classSpecs[TOP_SUCCESSIVE_NAME]?.addType(typeSpec.build())
339 | }; return classSpecs[TOP_SUCCESSIVE_NAME]?.build() ?: SError.make("Merge accessors classes failed")
340 | }
341 |
342 | /**
343 | * 创建首位构建器
344 | * @param configs 当前配置
345 | * @throws IllegalStateException 如果名称为空
346 | */
347 | private fun createTopClassSpec(configs: ISweetPropertyConfigs.IBuildScriptGenerateConfigs) {
348 | if (configs.name.isBlank()) SError.make("Class name cannot be empty or blank")
349 | this.configs = configs
350 | val topClassName = "${configs.name.replace(":", "_").uppercamelcase()}$TOP_CLASS_SUFFIX_NAME"
351 | memoryExtensionClasses[configs.name] = "$ACCESSORS_PACKAGE_NAME.$topClassName"
352 | classSpecs[TOP_SUCCESSIVE_NAME] = createClassSpec(topClassName, isInner = false)
353 | constructorSpecs[TOP_SUCCESSIVE_NAME] = createConstructorSpec()
354 | }
355 |
356 | /**
357 | * 清空所有已生成的数据
358 | * @param isClearAll 是否全部清空 - 包括添加的 [memoryExtensionClasses] - 默认否
359 | */
360 | private fun clearGeneratedData(isClearAll: Boolean = false) {
361 | classSpecs.clear()
362 | constructorSpecs.clear()
363 | preAddConstructorSpecNames.clear()
364 | grandSuccessiveNames.clear()
365 | grandSuccessiveDuplicateIndexs.clear()
366 | usedSuccessiveMethods.clear()
367 | usedSuccessiveTags.clear()
368 | if (isClearAll) memoryExtensionClasses.clear()
369 | }
370 |
371 | /**
372 | * 生成 [JavaFile] 数组
373 | *
374 | * - 注意:[allConfigs] 与 [allKeyValues] 数量必须相等
375 | * @param allConfigs 全部配置实例
376 | * @param allKeyValues 全部键值数组
377 | * @return [MutableList]<[JavaFile]>
378 | * @throws IllegalStateException 如果生成失败
379 | */
380 | internal fun build(
381 | allConfigs: MutableList,
382 | allKeyValues: MutableList
383 | ) = runCatching {
384 | val files = mutableListOf()
385 | if (allConfigs.size != allKeyValues.size) SError.make("Invalid build arguments")
386 | if (allConfigs.isEmpty()) return@runCatching files
387 | clearGeneratedData(isClearAll = true)
388 | allConfigs.forEachIndexed { index, configs ->
389 | val keyValues = allKeyValues[index]
390 | clearGeneratedData()
391 | createTopClassSpec(configs)
392 | keyValues.toOptimize().forEach { (key, value) ->
393 | parseTypeSpec(key, value.first, value.second)
394 | releaseParseTypeSpec()
395 | }; files.add(buildTypeSpec().createJavaFile(ACCESSORS_PACKAGE_NAME))
396 | }; files
397 | }.getOrElse { SError.make("Failed to generated accessors classes\n$it") }
398 |
399 | /**
400 | * 获取参与编译的 Stub [JavaFile] 数组
401 | * @return [List]<[JavaFile]>
402 | */
403 | internal val compileStubFiles get(): List {
404 | val stubFiles = mutableListOf()
405 | val nonnullFile =
406 | TypeSpec.annotationBuilder(Nonnull::class.java.simpleName)
407 | .addModifiers(Modifier.PUBLIC)
408 | .build().createJavaFile(Nonnull::class.java.packageName)
409 | val iExtensionAccessorsFile =
410 | TypeSpec.interfaceBuilder(IExtensionAccessors::class.java.simpleName)
411 | .addModifiers(Modifier.PUBLIC)
412 | .build().createJavaFile(IExtensionAccessors::class.java.packageName)
413 | stubFiles.add(nonnullFile)
414 | stubFiles.add(iExtensionAccessorsFile)
415 | return stubFiles
416 | }
417 |
418 | /**
419 | * 获取扩展功能预置 [Class]
420 | * @param name 名称
421 | * @return [String]
422 | * @throws IllegalStateException 如果 [Class] 不存在
423 | */
424 | internal fun propertiesClass(name: String) = memoryExtensionClasses[name] ?: SError.make("Could not found class \"$name\"")
425 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/generator/PropertiesSourcesGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.plugin.generator
23 |
24 | import com.highcapable.sweetproperty.SweetProperty
25 | import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
26 | import com.highcapable.sweetproperty.plugin.generator.factory.PropertyMap
27 | import com.highcapable.sweetproperty.plugin.generator.factory.parseTypedValue
28 | import com.highcapable.sweetproperty.plugin.generator.factory.toOptimize
29 | import com.highcapable.sweetproperty.plugin.generator.factory.toUnderscores
30 | import com.highcapable.sweetproperty.utils.debug.SError
31 | import com.highcapable.sweetproperty.utils.firstNumberToLetter
32 | import com.squareup.kotlinpoet.FileSpec
33 | import com.squareup.kotlinpoet.KModifier
34 | import com.squareup.kotlinpoet.PropertySpec
35 | import com.squareup.kotlinpoet.TypeSpec
36 | import java.text.SimpleDateFormat
37 | import java.util.*
38 |
39 | /**
40 | * 属性键值代码生成实现类
41 | */
42 | internal class PropertiesSourcesGenerator {
43 |
44 | /**
45 | * 生成 [FileSpec]
46 | * @param configs 当前配置实例
47 | * @param keyValues 键值数组
48 | * @param packageName 包名
49 | * @param className 类名
50 | * @return [FileSpec]
51 | * @throws IllegalStateException 如果生成失败
52 | */
53 | internal fun build(
54 | configs: ISweetPropertyConfigs.ISourcesCodeGenerateConfigs,
55 | keyValues: PropertyMap,
56 | packageName: String,
57 | className: String
58 | ) = runCatching {
59 | FileSpec.builder(packageName, className).apply {
60 | addType(TypeSpec.objectBuilder(className).apply {
61 | addKdoc(
62 | """
63 | This class is generated by ${SweetProperty.TAG} at ${SimpleDateFormat.getDateTimeInstance().format(Date())}
64 |
65 | The content here is automatically generated according to the properties of your projects
66 |
67 | You can visit [here](${SweetProperty.PROJECT_URL}) for more help
68 | """.trimIndent()
69 | )
70 | if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
71 | keyValues.toOptimize().toUnderscores().forEach { (key, value) ->
72 | val typedValue = value.second.parseTypedValue(configs.isEnableTypeAutoConversion)
73 | addProperty(PropertySpec.builder(key.firstNumberToLetter(), typedValue.first).apply {
74 | addKdoc("Resolve the \"${value.first.toKotlinPoetNoEscape()}\" value ${typedValue.second.toKotlinPoetNoEscape()}")
75 | if (configs.isEnableRestrictedAccess) addModifiers(KModifier.INTERNAL)
76 | addModifiers(KModifier.CONST)
77 | initializer(typedValue.second.toKotlinPoetNoEscape().toKotlinPoetSpace())
78 | }.build())
79 | }
80 | }.build())
81 | }.build()
82 | }.getOrElse { SError.make("Failed to generated Kotlin file\n$it") }
83 |
84 | /**
85 | * 转换到 KotlinPoet 声明的空格
86 | * @return [String]
87 | */
88 | private fun String.toKotlinPoetSpace() = replace(" ", "·")
89 |
90 | /**
91 | * 转换到 KotlinPoet 非转义字符内容
92 | * @return [String]
93 | */
94 | private fun String.toKotlinPoetNoEscape() = replace("%", "%%")
95 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/generator/factory/GeneratorFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.plugin.generator.factory
23 |
24 | import com.highcapable.sweetproperty.utils.underscore
25 | import kotlin.reflect.KClass
26 |
27 | /** 属性键值数组类型定义 */
28 | internal typealias PropertyMap = MutableMap
29 |
30 | /** 属性键值优化数组类型定义 */
31 | internal typealias PropertyOptimizeMap = MutableMap>
32 |
33 | /** 属性键值规则类型定义 */
34 | internal typealias PropertyValueRule = (value: String) -> String
35 |
36 | /**
37 | * 移除键值内容自动转换类型的引号
38 | * @return [String]
39 | */
40 | internal fun String.removeAutoConversion() = removeSurrounding("\"").removeSurrounding("'")
41 |
42 | /**
43 | * 解析到键值内容类型
44 | * @param isAutoConversion 是否自动转换类型
45 | * @return [Pair]<[KClass], [String]>
46 | */
47 | internal fun Any.parseTypedValue(isAutoConversion: Boolean): Pair, String> {
48 | var isStringType = false
49 | val valueString = toString()
50 | .replace("\n", "\\n")
51 | .replace("\r", "\\r")
52 | .replace("\\", "\\\\")
53 | .let {
54 | if (isAutoConversion && (it.startsWith("\"") && it.endsWith("\"") || it.startsWith("'") && it.endsWith("'"))) {
55 | isStringType = true
56 | it.drop(1).dropLast(1)
57 | } else it.replace("\"", "\\\"")
58 | }
59 | if (!isAutoConversion) return Pair(String::class, "\"$valueString\"")
60 | val typeSpec = when {
61 | isStringType -> String::class
62 | valueString.trim().toIntOrNull() != null -> Int::class
63 | valueString.trim().toLongOrNull() != null -> Long::class
64 | valueString.trim().toDoubleOrNull() != null -> Double::class
65 | valueString.trim().toFloatOrNull() != null -> Float::class
66 | valueString.trim() == "true" || valueString.trim() == "false" -> Boolean::class
67 | else -> String::class
68 | }; return Pair(typeSpec, if (typeSpec == String::class) "\"$valueString\"" else valueString.let {
69 | if (typeSpec == Long::class && !it.endsWith("L")) "${it}L" else it
70 | })
71 | }
72 |
73 | /**
74 | * [PropertyMap] 转换到 [PropertyOptimizeMap]
75 | *
76 | * 替换可能的键值名称特殊字符内容并保留原始键值名称
77 | * @return [PropertyOptimizeMap]
78 | */
79 | internal fun PropertyMap.toOptimize(): PropertyOptimizeMap {
80 | val newMap: PropertyOptimizeMap = mutableMapOf()
81 | var uniqueNumber = 1
82 | forEach { (key, value) ->
83 | var newKey = key.replace("\\W".toRegex(), "_")
84 | while (newMap.containsKey(newKey)) newKey = "$newKey${++uniqueNumber}"
85 | newMap[newKey] = key to value
86 | }; return newMap
87 | }
88 |
89 | /**
90 | * [PropertyOptimizeMap] 转换为大写下划线命名
91 | * @return [PropertyOptimizeMap]
92 | */
93 | internal fun PropertyOptimizeMap.toUnderscores(): PropertyOptimizeMap {
94 | val newMap: PropertyOptimizeMap = mutableMapOf()
95 | var uniqueNumber = 1
96 | forEach { (key, value) ->
97 | var newKey = key.underscore()
98 | while (newMap.containsKey(newKey)) newKey = "$newKey${++uniqueNumber}"
99 | newMap[newKey] = value.first to value.second
100 | }; return newMap
101 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/helper/PluginUpdateHelper.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/9/26.
21 | */
22 | package com.highcapable.sweetproperty.plugin.helper
23 |
24 | import com.highcapable.sweetproperty.SweetProperty
25 | import com.highcapable.sweetproperty.generated.SweetPropertyProperties
26 | import com.highcapable.sweetproperty.utils.debug.SLog
27 | import com.highcapable.sweetproperty.utils.executeUrlBody
28 | import org.gradle.api.initialization.Settings
29 | import org.xml.sax.InputSource
30 | import java.io.StringReader
31 | import javax.xml.parsers.DocumentBuilderFactory
32 |
33 | /**
34 | * 插件自身检查更新工具类
35 | */
36 | internal object PluginUpdateHelper {
37 |
38 | /** OSS Release URL 地址 */
39 | private const val SONATYPE_OSS_RELEASES_URL = "https://s01.oss.sonatype.org/content/repositories/releases"
40 |
41 | /** 依赖配置文件名 */
42 | private const val METADATA_FILE_NAME = "maven-metadata.xml"
43 |
44 | /** 插件自身依赖 URL 名称 */
45 | private val groupUrlNotation =
46 | "${SweetPropertyProperties.PROJECT_GROUP_NAME.replace(".","/")}/${SweetPropertyProperties.GRADLE_PLUGIN_MODULE_NAME}"
47 |
48 | /** 检查更新 URL 地址 */
49 | private val releaseUrl = "$SONATYPE_OSS_RELEASES_URL/$groupUrlNotation/$METADATA_FILE_NAME"
50 |
51 | /**
52 | * 检查更新
53 | * @param settings 当前设置
54 | */
55 | internal fun checkingForUpdate(settings: Settings) {
56 | if (settings.gradle.startParameter.isOffline) return
57 | val latestVersion = releaseUrl.executeUrlBody(isShowFailure = false).trim().findLatest()
58 | if (latestVersion.isNotBlank() && latestVersion != SweetProperty.VERSION) SLog.note(
59 | """
60 | Plugin update is available, the current version is ${SweetProperty.VERSION}, please update to $latestVersion
61 | You can modify your plugin version in your project's settings.gradle or settings.gradle.kts
62 | plugins {
63 | id("${SweetPropertyProperties.PROJECT_GROUP_NAME}") version "$latestVersion"
64 | ...
65 | }
66 | For more information, you can visit ${SweetProperty.PROJECT_URL}
67 | """.trimIndent(), SLog.UP
68 | )
69 | }
70 |
71 | /**
72 | * 解析 [METADATA_FILE_NAME] 内容并获取 "latest"
73 | * @return [String]
74 | */
75 | private fun String.findLatest() = runCatching {
76 | if (!(contains("")) || !endsWith(" ")) return@runCatching ""
77 | DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(InputSource(StringReader(this))).let { document ->
78 | document.getElementsByTagName("latest")?.let { if (it.length > 0) it.item(0)?.textContent ?: "" else "" }
79 | }
80 | }.getOrNull() ?: ""
81 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/plugin/helper/PropertiesDeployHelper.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/30.
21 | */
22 | package com.highcapable.sweetproperty.plugin.helper
23 |
24 | import com.highcapable.sweetproperty.SweetProperty
25 | import com.highcapable.sweetproperty.generated.SweetPropertyProperties
26 | import com.highcapable.sweetproperty.gradle.entity.ProjectDescriptor
27 | import com.highcapable.sweetproperty.gradle.factory.addDependencyToBuildScript
28 | import com.highcapable.sweetproperty.gradle.factory.fullName
29 | import com.highcapable.sweetproperty.gradle.factory.get
30 | import com.highcapable.sweetproperty.gradle.factory.getOrCreate
31 | import com.highcapable.sweetproperty.gradle.factory.hasExtension
32 | import com.highcapable.sweetproperty.gradle.factory.loadBuildScriptClass
33 | import com.highcapable.sweetproperty.plugin.config.factory.with
34 | import com.highcapable.sweetproperty.plugin.config.proxy.ISweetPropertyConfigs
35 | import com.highcapable.sweetproperty.plugin.config.type.GenerateLocationType
36 | import com.highcapable.sweetproperty.plugin.generator.PropertiesAccessorsGenerator
37 | import com.highcapable.sweetproperty.plugin.generator.PropertiesSourcesGenerator
38 | import com.highcapable.sweetproperty.plugin.generator.factory.PropertyMap
39 | import com.highcapable.sweetproperty.plugin.generator.factory.removeAutoConversion
40 | import com.highcapable.sweetproperty.utils.camelcase
41 | import com.highcapable.sweetproperty.utils.code.entity.MavenPomData
42 | import com.highcapable.sweetproperty.utils.code.factory.compile
43 | import com.highcapable.sweetproperty.utils.debug.SError
44 | import com.highcapable.sweetproperty.utils.flatted
45 | import com.highcapable.sweetproperty.utils.hasInterpolation
46 | import com.highcapable.sweetproperty.utils.isEmpty
47 | import com.highcapable.sweetproperty.utils.noBlank
48 | import com.highcapable.sweetproperty.utils.noEmpty
49 | import com.highcapable.sweetproperty.utils.replaceInterpolation
50 | import com.highcapable.sweetproperty.utils.toStringMap
51 | import com.highcapable.sweetproperty.utils.uppercamelcase
52 | import org.gradle.api.DomainObjectCollection
53 | import org.gradle.api.Project
54 | import org.gradle.api.initialization.Settings
55 | import java.io.File
56 | import java.io.FileReader
57 | import java.util.*
58 | import kotlin.properties.Delegates
59 |
60 | /**
61 | * 属性键值部署工具类
62 | */
63 | internal object PropertiesDeployHelper {
64 |
65 | /** 属性键值可访问 [Class] 标识名称 */
66 | private const val ACCESSORS_NAME = "properties-accessors"
67 |
68 | /** 属性键值默认生成包名 */
69 | private const val DEFAULT_PACKAGE_NAME = "${SweetPropertyProperties.PROJECT_GROUP_NAME}.defaultproperties"
70 |
71 | /** 当前配置实例 */
72 | private var configs by Delegates.notNull()
73 |
74 | /** 当前缓存的属性键值数组 (初始化装载) */
75 | private var cachedSettingsProperties = mutableListOf()
76 |
77 | /** 当前缓存的属性键值数组 (每个项目) */
78 | private var cachedProjectProperties = mutableMapOf()
79 |
80 | /** 上次修改的 Hash Code */
81 | private var lastModifiedHashCode = 0
82 |
83 | /** 配置是否已被修改 */
84 | private var isConfigsModified = true
85 |
86 | /** 属性键值可访问 [Class] 生成目录 */
87 | private var accessorsDir by Delegates.notNull()
88 |
89 | /** 属性键值可访问 [Class] 虚拟依赖数据 */
90 | private val accessorsPomData = MavenPomData(SweetPropertyProperties.PROJECT_GROUP_NAME, ACCESSORS_NAME, SweetProperty.VERSION)
91 |
92 | /** 属性键值可访问 [Class] 生成实例 */
93 | private val accessorsGenerator = PropertiesAccessorsGenerator()
94 |
95 | /** 属性键值代码生成实例 */
96 | private val sourcesGenerator = PropertiesSourcesGenerator()
97 |
98 | /**
99 | * 装载并初始化
100 | * @param settings 当前设置
101 | * @param configs 当前配置
102 | */
103 | internal fun initialize(settings: Settings, configs: ISweetPropertyConfigs) {
104 | this.configs = configs
105 | checkingConfigsModified(settings)
106 | if (!configs.isEnable) return
107 | generatedAccessors(settings)
108 | }
109 |
110 | /**
111 | * 开始处理
112 | * @param rootProject 当前根项目
113 | */
114 | internal fun resolve(rootProject: Project) {
115 | if (!configs.isEnable) return
116 | resolveAccessors(rootProject)
117 | }
118 |
119 | /**
120 | * 开始部署
121 | * @param rootProject 当前根项目
122 | */
123 | internal fun deploy(rootProject: Project) {
124 | if (!configs.isEnable) return
125 | deployAccessors(rootProject)
126 | deploySourcesCode(rootProject)
127 | }
128 |
129 | /**
130 | * 检查配置是否已被修改
131 | * @param settings 当前设置
132 | */
133 | private fun checkingConfigsModified(settings: Settings) {
134 | settings.settingsDir.also { dir ->
135 | val groovyHashCode = dir.resolve("settings.gradle").takeIf { it.exists() }?.readText()?.hashCode()
136 | val kotlinHashCode = dir.resolve("settings.gradle.kts").takeIf { it.exists() }?.readText()?.hashCode()
137 | val gradleHashCode = groovyHashCode ?: kotlinHashCode ?: -1
138 | isConfigsModified = gradleHashCode == -1 || lastModifiedHashCode != gradleHashCode
139 | lastModifiedHashCode = gradleHashCode
140 | }
141 | }
142 |
143 | /**
144 | * 生成构建脚本代码
145 | * @param settings 当前设置
146 | */
147 | private fun generatedAccessors(settings: Settings) {
148 | accessorsDir = generatedAccessorsDir(settings)
149 | val allConfigs = mutableListOf()
150 | val allProperties = mutableListOf()
151 | if (configs.global.buildScript.isEnable) {
152 | allProperties.add(generatedProperties(configs.global.buildScript, ProjectDescriptor.create(settings)))
153 | allConfigs.add(configs.global.buildScript)
154 | }
155 | configs.projects.forEach { (name, subConfigs) ->
156 | if (!subConfigs.buildScript.isEnable) return@forEach
157 | allProperties.add(generatedProperties(subConfigs.buildScript, ProjectDescriptor.create(settings, name)))
158 | allConfigs.add(subConfigs.buildScript)
159 | }
160 | if (!isConfigsModified &&
161 | allProperties == cachedSettingsProperties &&
162 | !accessorsDir.resolve(accessorsPomData.relativePomPath).isEmpty()
163 | ) return
164 | cachedSettingsProperties = allProperties
165 | accessorsGenerator.build(allConfigs, allProperties).compile(accessorsPomData, accessorsDir.absolutePath, accessorsGenerator.compileStubFiles)
166 | }
167 |
168 | /**
169 | * 处理构建脚本代码
170 | * @param rootProject 当前根项目
171 | */
172 | private fun resolveAccessors(rootProject: Project) {
173 | if (!accessorsDir.resolve(accessorsPomData.relativePomPath).isEmpty())
174 | rootProject.addDependencyToBuildScript(accessorsDir.absolutePath, accessorsPomData)
175 | }
176 |
177 | /**
178 | * 部署构建脚本代码
179 | * @param rootProject 当前根项目
180 | */
181 | private fun deployAccessors(rootProject: Project) {
182 | /** 部署扩展方法 */
183 | fun Project.deploy() {
184 | val configs = configs.with(this).buildScript
185 | if (!configs.isEnable) return
186 | val className = accessorsGenerator.propertiesClass(configs.name)
187 | val accessorsClass = loadBuildScriptClass(className) ?: SError.make(
188 | """
189 | Generated class "$className" not found, stop loading $this
190 | Please check whether the initialization process is interrupted and re-run Gradle Sync
191 | If this doesn't work, please manually delete the entire "${accessorsDir.absolutePath}" directory
192 | """.trimIndent()
193 | )
194 | getOrCreate(configs.extensionName.camelcase(), accessorsClass)
195 | }
196 | rootProject.deploy()
197 | rootProject.subprojects.forEach { it.deploy() }
198 | }
199 |
200 | /**
201 | * 部署项目代码
202 | * @param rootProject 当前根项目
203 | */
204 | private fun deploySourcesCode(rootProject: Project) {
205 | /** 生成代码 */
206 | fun Project.generate() {
207 | val configs = configs.with(this).sourcesCode
208 | val outputDir = file(configs.generateDirPath)
209 | if (!configs.isEnable) return
210 | val properties = generatedProperties(configs, ProjectDescriptor.create(project = this))
211 | if (!isConfigsModified && properties == cachedProjectProperties[fullName()] && !outputDir.isEmpty()) {
212 | if (configs.isEnable) configureSourceSets(project = this)
213 | return
214 | }; outputDir.apply { if (exists()) deleteRecursively() }
215 | cachedProjectProperties[fullName()] = properties
216 | val packageName = generatedPackageName(configs, project = this)
217 | val className = generatedClassName(configs, project = this)
218 | sourcesGenerator.build(configs, properties, packageName, className).writeTo(outputDir)
219 | configureSourceSets(project = this)
220 | }
221 | rootProject.generate()
222 | rootProject.subprojects.forEach { it.afterEvaluate { generate() } }
223 | }
224 |
225 | /**
226 | * 配置项目 SourceSets
227 | * @param project 当前项目
228 | */
229 | private fun configureSourceSets(project: Project) {
230 | fun Project.deploySourceSets(name: String) = runCatching {
231 | val extension = get(name)
232 | val collection = extension.javaClass.getMethod("getSourceSets").invoke(extension) as DomainObjectCollection<*>
233 | collection.configureEach {
234 | val kotlin = javaClass.getMethod("getKotlin").invoke(this)
235 | kotlin.javaClass.getMethod("srcDir", Any::class.java).invoke(kotlin, configs.with(project).sourcesCode.generateDirPath)
236 | }
237 | }
238 | if (project.hasExtension("kotlin")) project.deploySourceSets(name = "kotlin")
239 | if (project.hasExtension("android")) project.deploySourceSets(name = "android")
240 | }
241 |
242 | /**
243 | * 获取属性键值可访问 [Class] 生成目录
244 | * @param settings 当前设置
245 | * @return [File]
246 | */
247 | private fun generatedAccessorsDir(settings: Settings) =
248 | settings.rootDir.resolve(".gradle").resolve(SweetPropertyProperties.PROJECT_MODULE_NAME).resolve(ACCESSORS_NAME).apply { mkdirs() }
249 |
250 | /**
251 | * 获取生成的属性键值数组
252 | * @param configs 当前配置
253 | * @param descriptor 当前描述
254 | * @return [PropertyMap]
255 | */
256 | private fun generatedProperties(configs: ISweetPropertyConfigs.IBaseGenerateConfigs, descriptor: ProjectDescriptor): PropertyMap {
257 | val propteries = mutableMapOf()
258 | configs.permanentKeyValues.forEach { (key, value) -> propteries[key] = value }
259 | configs.generateLocationTypes.forEach {
260 | mutableMapOf().apply {
261 | when (it) {
262 | GenerateLocationType.CURRENT_PROJECT -> createProperties(configs, descriptor.currentDir).forEach { putAll(it) }
263 | GenerateLocationType.ROOT_PROJECT -> createProperties(configs, descriptor.rootDir).forEach { putAll(it) }
264 | GenerateLocationType.GLOBAL -> createProperties(configs, descriptor.homeDir).forEach { putAll(it) }
265 | GenerateLocationType.SYSTEM -> putAll(System.getProperties())
266 | GenerateLocationType.SYSTEM_ENV -> putAll(System.getenv())
267 | }
268 | }.filter { (key, value) ->
269 | if (configs.isEnableExcludeNonStringValue)
270 | key is CharSequence && key.isNotBlank() && value is CharSequence
271 | else key.toString().isNotBlank() && value != null
272 | }.toStringMap().filter { (key, _) ->
273 | configs.includeKeys.noEmpty()?.any { content ->
274 | when (content) {
275 | is Regex -> content.matches(key)
276 | else -> content.toString() == key
277 | }
278 | } ?: true
279 | }.filter { (key, _) ->
280 | configs.excludeKeys.noEmpty()?.none { content ->
281 | when (content) {
282 | is Regex -> content.matches(key)
283 | else -> content.toString() == key
284 | }
285 | } ?: true
286 | }.toMutableMap().also { resolveKeyValues ->
287 | resolveKeyValues.onEach { (key, value) ->
288 | val resolveKeys = mutableListOf()
289 |
290 | /**
291 | * 处理键值内容
292 | * @return [String]
293 | */
294 | fun String.resolveValue(): String = replaceInterpolation { matchKey ->
295 | if (resolveKeys.size > 5) SError.make("Key \"$key\" has been called recursively multiple times of those $resolveKeys")
296 | resolveKeys.add(matchKey)
297 | var resolveValue = if (configs.isEnableValueInterpolation) resolveKeyValues[matchKey] ?: "" else matchKey
298 | resolveValue = resolveValue.removeAutoConversion()
299 | if (resolveValue.hasInterpolation()) resolveValue.resolveValue()
300 | else resolveValue
301 | }
302 | if (value.hasInterpolation()) resolveKeyValues[key] = value.resolveValue()
303 | }.takeIf { configs.keyValuesRules.isNotEmpty() }?.forEach { (key, value) ->
304 | configs.keyValuesRules[key]?.also { resolveKeyValues[key] = it(value) }
305 | }; propteries.putAll(resolveKeyValues)
306 | }
307 | }; return propteries
308 | }
309 |
310 | /**
311 | * 获取生成的包名
312 | * @param configs 当前配置
313 | * @param project 当前项目
314 | * @return [String]
315 | */
316 | private fun generatedPackageName(configs: ISweetPropertyConfigs.ISourcesCodeGenerateConfigs, project: Project): String {
317 | /**
318 | * 获取 Android 项目的 "namespace"
319 | * @return [String] or null
320 | */
321 | fun Project.namespace() = runCatching {
322 | val extension = get("android")
323 | extension.javaClass.getMethod("getNamespace").invoke(extension) as String
324 | }.getOrNull()
325 | val packageName = configs.packageName.noBlank()
326 | ?: project.namespace()
327 | ?: project.group.toString().noBlank()
328 | ?: "$DEFAULT_PACKAGE_NAME.${project.fullName(isUseColon = false).replace(":", "").flatted()}"
329 | return "$packageName.generated"
330 | }
331 |
332 | /**
333 | * 获取生成的类名
334 | * @param configs 当前配置
335 | * @param project 当前项目
336 | * @return [String]
337 | */
338 | private fun generatedClassName(configs: ISweetPropertyConfigs.ISourcesCodeGenerateConfigs, project: Project): String {
339 | val className = configs.className.noBlank()
340 | ?: project.fullName(isUseColon = false).replace(":", "_").uppercamelcase().noBlank()
341 | ?: "Undefined"
342 | return "${className.uppercamelcase()}Properties"
343 | }
344 |
345 | /**
346 | * 创建新的 [Properties] 数组
347 | * @param configs 当前配置
348 | * @param dir 当前目录
349 | * @return [MutableList]<[Properties]>
350 | */
351 | private fun createProperties(configs: ISweetPropertyConfigs.IBaseGenerateConfigs, dir: File?) = runCatching {
352 | mutableListOf().apply {
353 | configs.propertiesFileNames.forEach {
354 | val propertiesFile = dir?.resolve(it)
355 | if (propertiesFile?.exists() == true) add(Properties().apply { load(FileReader(propertiesFile.absolutePath)) })
356 | }
357 | }
358 | }.getOrNull() ?: mutableListOf()
359 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/utils/FileFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | @file:Suppress("unused")
23 |
24 | package com.highcapable.sweetproperty.utils
25 |
26 | import java.io.File
27 |
28 | /**
29 | * 字符串路径转换为文件
30 | *
31 | * 自动调用 [parseFileSeparator]
32 | * @return [File]
33 | */
34 | internal fun String.toFile() = File(parseFileSeparator())
35 |
36 | /**
37 | * 格式化到当前操作系统的文件分隔符
38 | * @return [String]
39 | */
40 | internal fun String.parseFileSeparator() = replace("/", File.separator).replace("\\", File.separator)
41 |
42 | /**
43 | * 格式化到 Unix 操作系统的文件分隔符
44 | * @return [String]
45 | */
46 | internal fun String.parseUnixFileSeparator() = replace("\\", "/")
47 |
48 | /**
49 | * 检查目录是否为空
50 | *
51 | * - 如果不是目录 (可能是文件) - 返回 true
52 | * - 如果文件不存在 - 返回 true
53 | * @return [Boolean]
54 | */
55 | internal fun File.isEmpty() = !exists() || !isDirectory || listFiles().isNullOrEmpty()
56 |
57 | /** 删除目录下的空子目录 */
58 | internal fun File.deleteEmptyRecursively() {
59 | listFiles { file -> file.isDirectory }?.forEach { subDir ->
60 | subDir.deleteEmptyRecursively()
61 | if (subDir.listFiles()?.isEmpty() == true) subDir.delete()
62 | }
63 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/utils/HttpFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/9/26.
21 | */
22 | package com.highcapable.sweetproperty.utils
23 |
24 | import com.highcapable.sweetproperty.utils.debug.SError
25 | import com.highcapable.sweetproperty.utils.debug.SLog
26 | import okhttp3.Credentials
27 | import okhttp3.OkHttpClient
28 | import okhttp3.Request
29 | import java.util.concurrent.TimeUnit
30 |
31 | /**
32 | * 获取当前 URL 地址的请求体字符串内容 (GET) (同步)
33 | * @param username 用户名
34 | * @param password 密码
35 | * @param isShowFailure 是否显示错误 - 默认是
36 | * @return [String]
37 | */
38 | internal fun String.executeUrlBody(username: String = "", password: String = "", isShowFailure: Boolean = true) = runCatching {
39 | OkHttpClient()
40 | .newBuilder()
41 | .connectTimeout(10000, TimeUnit.MILLISECONDS)
42 | .authenticator { _, response ->
43 | if (response.code == 400 || response.code == 401)
44 | response.request.newBuilder()
45 | .header("Authorization", Credentials.basic(username, password))
46 | .build()
47 | else null
48 | }.build().newCall(
49 | Request.Builder().url(when {
50 | startsWith("https://") -> "https://" + replace("https://", "").replace("//", "/")
51 | startsWith("http://") -> "http://" + replace("http://", "").replace("//", "/")
52 | else -> SError.make("Invalid URL: $this")
53 | }).get().build()
54 | ).execute().let {
55 | if (it.code == 200 || it.code == 404) it.body?.string() ?: ""
56 | else SError.make("Request failed with code ${it.code}")
57 | }
58 | }.onFailure { if (isShowFailure) SLog.error("Failed to connect to $this\n$it") }.getOrNull() ?: ""
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/utils/VariableFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/26.
21 | */
22 | package com.highcapable.sweetproperty.utils
23 |
24 | /**
25 | * 转换当前 [Map] 键值到字符串类型
26 | * @return [Map]<[String], [String]>
27 | */
28 | internal inline fun Map.toStringMap() = mapKeys { e -> e.key.toString() }.mapValues { e -> e.value.toString() }
29 |
30 | /**
31 | * 当数组不为空时返回非空
32 | * @return [T] or null
33 | */
34 | internal inline fun > T.noEmpty() = takeIf { it.isNotEmpty() }
35 |
36 | /**
37 | * 当字符串不为空白时返回非空
38 | * @return [T] or null
39 | */
40 | internal inline fun T.noBlank() = takeIf { it.isNotBlank() }
41 |
42 | /**
43 | * 扁平化字符串处理
44 | *
45 | * 移除所有空格并转换为小写字母
46 | * @return [String]
47 | */
48 | internal fun String.flatted() = replace(" ", "").lowercase()
49 |
50 | /**
51 | * 驼峰、"-"、"." 转大写下划线命名
52 | * @return [String]
53 | */
54 | internal fun String.underscore() = replace(".", "_").replace("-", "_").replace(" ", "_").replace("([a-z])([A-Z]+)".toRegex(), "$1_$2").uppercase()
55 |
56 | /**
57 | * 下划线、分隔线、点、空格命名字符串转小驼峰命名字符串
58 | * @return [String]
59 | */
60 | internal fun String.camelcase() = runCatching {
61 | split("_", ".", "-", " ").map { it.replaceFirstChar { e -> e.titlecase() } }.let { words ->
62 | words.first().replaceFirstChar { it.lowercase() } + words.drop(1).joinToString("")
63 | }
64 | }.getOrNull() ?: this
65 |
66 | /**
67 | * 下划线、分隔线、点、空格命名字符串转大驼峰命名字符串
68 | * @return [String]
69 | */
70 | internal fun String.uppercamelcase() = camelcase().capitalize()
71 |
72 | /**
73 | * 字符串首字母大写
74 | * @return [String]
75 | */
76 | internal fun String.capitalize() = replaceFirstChar { it.uppercaseChar() }
77 |
78 | /**
79 | * 字符串首字母小写
80 | * @return [String]
81 | */
82 | internal fun String.uncapitalize() = replaceFirstChar { it.lowercaseChar() }
83 |
84 | /**
85 | * 转换字符串第一位数字到外观近似大写字母
86 | * @return [String]
87 | */
88 | internal fun String.firstNumberToLetter() =
89 | if (isNotBlank()) (mapOf(
90 | '0' to 'O', '1' to 'I',
91 | '2' to 'Z', '3' to 'E',
92 | '4' to 'A', '5' to 'S',
93 | '6' to 'G', '7' to 'T',
94 | '8' to 'B', '9' to 'P'
95 | )[first()] ?: first()) + substring(1)
96 | else this
97 |
98 | /**
99 | * 转换字符串为非 Java 关键方法引用名称
100 | * @return [String]
101 | */
102 | internal fun String.toNonJavaName() = if (lowercase() == "class") replace("lass", "lazz") else this
103 |
104 | /**
105 | * 字符串中是否存在插值符号 ${...}
106 | * @return [Boolean]
107 | */
108 | internal fun String.hasInterpolation() = contains("\${") && contains("}")
109 |
110 | /**
111 | * 替换字符串中的插值符号 ${...}
112 | * @param result 回调结果
113 | * @return [String]
114 | */
115 | internal fun String.replaceInterpolation(result: (groupValue: String) -> CharSequence) =
116 | "\\$\\{(.+?)}".toRegex().replace(this) { result(it.groupValues[1]) }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/utils/code/CodeCompiler.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.utils.code
23 |
24 | import com.highcapable.sweetproperty.utils.code.entity.MavenPomData
25 | import com.highcapable.sweetproperty.utils.debug.SError
26 | import com.highcapable.sweetproperty.utils.deleteEmptyRecursively
27 | import com.highcapable.sweetproperty.utils.toFile
28 | import net.lingala.zip4j.ZipFile
29 | import net.lingala.zip4j.model.ZipParameters
30 | import java.io.File
31 | import javax.tools.DiagnosticCollector
32 | import javax.tools.JavaFileObject
33 | import javax.tools.StandardLocation
34 | import javax.tools.ToolProvider
35 |
36 | /**
37 | * 代码编译处理类
38 | */
39 | internal object CodeCompiler {
40 |
41 | /** Maven 模型版本 */
42 | private const val MAVEN_MODEL_VERSION = "4.0.0"
43 |
44 | /**
45 | * 编译 [JavaFileObject] 为 Maven 依赖
46 | * @param pomData Maven POM 实体
47 | * @param outputDirPath 编译输出目录路径
48 | * @param files [JavaFileObject] 数组
49 | * @param compileOnlyFiles [JavaFileObject] 仅编译数组 - 默认空
50 | * @throws IllegalStateException 如果编译失败
51 | */
52 | internal fun compile(
53 | pomData: MavenPomData,
54 | outputDirPath: String,
55 | files: List,
56 | compileOnlyFiles: List = mutableListOf()
57 | ) {
58 | val outputDir = outputDirPath.toFile()
59 | if (files.isEmpty()) {
60 | if (outputDir.exists()) outputDir.deleteRecursively()
61 | return
62 | } else outputDir.also { if (!it.exists()) it.mkdirs() }
63 | val outputBuildDir = "$outputDirPath/build".toFile().also { if (it.exists()) it.deleteRecursively(); it.mkdirs() }
64 | val outputClassesDir = "${outputBuildDir.absolutePath}/classes".toFile().apply { mkdirs() }
65 | val outputSourcesDir = "${outputBuildDir.absolutePath}/sources".toFile().apply { mkdirs() }
66 | val compiler = ToolProvider.getSystemJavaCompiler()
67 | val diagnostics = DiagnosticCollector()
68 | val fileManager = compiler.getStandardFileManager(diagnostics, null, null)
69 | fileManager.setLocation(StandardLocation.CLASS_OUTPUT, listOf(outputClassesDir))
70 | val task = compiler.getTask(null, fileManager, diagnostics, null, null, compileOnlyFiles + files)
71 | val result = task.call()
72 | var diagnosticsMessage = ""
73 | diagnostics.diagnostics?.forEach { diagnostic ->
74 | diagnosticsMessage += " > Error on line ${diagnostic.lineNumber} in ${diagnostic.source?.toUri()}\n"
75 | diagnosticsMessage += " ${diagnostic.getMessage(null)}\n"
76 | }
77 | runCatching { fileManager.close() }
78 | compileOnlyFiles.forEach { "${outputClassesDir.absolutePath}/${it.name}".replace(".java", ".class").toFile().delete() }
79 | files.forEach {
80 | it.toFiles(outputSourcesDir).also { (sourceDir, sourceFile) ->
81 | sourceDir.mkdirs()
82 | sourceFile.writeText(it.getCharContent(true).toString())
83 | }
84 | }
85 | if (result) {
86 | outputClassesDir.deleteEmptyRecursively()
87 | writeMetaInf(outputClassesDir)
88 | writeMetaInf(outputSourcesDir)
89 | createJarAndPom(pomData, outputDir, outputBuildDir, outputClassesDir, outputSourcesDir)
90 | } else SError.make("Failed to compile java files into path: $outputDirPath\n$diagnosticsMessage")
91 | }
92 |
93 | /**
94 | * 打包 JAR 并写入 POM
95 | * @param pomData Maven POM 实体
96 | * @param outputDir 编译输出目录
97 | * @param buildDir 编译目录
98 | * @param classesDir 编译二进制目录
99 | * @param sourcesDir 编译源码目录
100 | */
101 | private fun createJarAndPom(pomData: MavenPomData, outputDir: File, buildDir: File, classesDir: File, sourcesDir: File) {
102 | val pomDir = outputDir.resolve(pomData.relativePomPath).also { if (!it.exists()) it.mkdirs() }
103 | packageToJar(classesDir, pomDir, pomData, isSourcesJar = false)
104 | packageToJar(sourcesDir, pomDir, pomData, isSourcesJar = true)
105 | writePom(pomDir, pomData)
106 | buildDir.deleteRecursively()
107 | }
108 |
109 | /**
110 | * 写入 META-INF/MANIFEST.MF
111 | * @param dir 当前目录
112 | */
113 | private fun writeMetaInf(dir: File) {
114 | val metaInfDir = dir.resolve("META-INF").apply { mkdirs() }
115 | metaInfDir.resolve("MANIFEST.MF").writeText("Manifest-Version: 1.0")
116 | }
117 |
118 | /**
119 | * 写入 POM
120 | * @param dir 当前目录
121 | * @param pomData Maven POM 实体
122 | */
123 | private fun writePom(dir: File, pomData: MavenPomData) =
124 | dir.resolve("${pomData.artifactId}-${pomData.version}.pom").writeText(
125 | """
126 |
127 |
131 | $MAVEN_MODEL_VERSION
132 | ${pomData.groupId}
133 | ${pomData.artifactId}
134 | ${pomData.version}
135 |
136 | """.trimIndent()
137 | )
138 |
139 | /**
140 | * 转换到文件
141 | * @param outputDir 输出目录
142 | * @return [Pair]<[File], [File]>
143 | */
144 | private fun JavaFileObject.toFiles(outputDir: File): Pair {
145 | val outputDirPath = outputDir.absolutePath
146 | val separator = if (name.contains("/")) "/" else "\\"
147 | val names = name.split(separator)
148 | val fileName = names[names.lastIndex]
149 | val folderName = name.replace(fileName, "")
150 | return "$outputDirPath/$folderName".toFile() to "$outputDirPath/$name".toFile()
151 | }
152 |
153 | /**
154 | * 打包编译输出目录到 JAR
155 | * @param buildDir 编译目录
156 | * @param outputDir 输出目录
157 | * @param pomData Maven POM 实体
158 | * @param isSourcesJar 是否为源码 JAR
159 | * @throws IllegalStateException 如果编译输出目录不存在
160 | */
161 | private fun packageToJar(buildDir: File, outputDir: File, pomData: MavenPomData, isSourcesJar: Boolean) {
162 | if (!buildDir.exists()) SError.make("Jar file output path not found: ${buildDir.absolutePath}")
163 | val jarFile = outputDir.resolve("${pomData.artifactId}-${pomData.version}${if (isSourcesJar) "-sources" else ""}.jar")
164 | if (jarFile.exists()) jarFile.delete()
165 | ZipFile(jarFile).addFolder(buildDir, ZipParameters().apply { isIncludeRootFolder = false })
166 | }
167 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/utils/code/entity/MavenPomData.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.utils.code.entity
23 |
24 | /**
25 | * Maven POM 实体
26 | * @param groupId Group ID
27 | * @param artifactId Artifact Id
28 | * @param version 版本
29 | */
30 | internal data class MavenPomData(internal val groupId: String, internal val artifactId: String, internal val version: String) {
31 |
32 | /**
33 | * 获取 [MavenPomData] 相对路径
34 | * @return [String]
35 | */
36 | internal val relativePomPath get() = "${groupId.toPomPathName()}/$artifactId/$version"
37 |
38 | /**
39 | * 转换到 [MavenPomData] 目录名称
40 | * @return [String]
41 | */
42 | private fun String.toPomPathName() = trim().replace(".", "/").replace("_", "/").replace(":", "/").replace("-", "/")
43 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/utils/code/factory/CodeCompilerFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | @file:Suppress("unused")
23 |
24 | package com.highcapable.sweetproperty.utils.code.factory
25 |
26 | import com.highcapable.sweetproperty.utils.code.CodeCompiler
27 | import com.highcapable.sweetproperty.utils.code.entity.MavenPomData
28 | import com.squareup.javapoet.JavaFile
29 | import javax.tools.JavaFileObject
30 |
31 | /**
32 | * 编译 [JavaFile] 为 Maven 依赖
33 | * @param pomData Maven POM 实体
34 | * @param outputDirPath 编译输出目录路径
35 | * @param compileOnlyFiles [JavaFile] 仅编译数组 - 默认空
36 | * @throws IllegalStateException 如果编译失败
37 | */
38 | @JvmName("compileWithJavaFile")
39 | internal fun JavaFile.compile(pomData: MavenPomData, outputDirPath: String, compileOnlyFiles: List = mutableListOf()) =
40 | CodeCompiler.compile(
41 | pomData = pomData,
42 | outputDirPath = outputDirPath,
43 | files = listOf(toJavaFileObject()),
44 | compileOnlyFiles = mutableListOf().also { compileOnlyFiles.forEach { e -> it.add(e.toJavaFileObject()) } }
45 | )
46 |
47 | /**
48 | * 编译 [JavaFile] 为 Maven 依赖
49 | * @param pomData Maven POM 实体
50 | * @param outputDirPath 编译输出目录路径
51 | * @param compileOnlyFiles [JavaFile] 仅编译数组 - 默认空
52 | * @throws IllegalStateException 如果编译失败
53 | */
54 | @JvmName("compileWithJavaFile")
55 | internal fun List.compile(pomData: MavenPomData, outputDirPath: String, compileOnlyFiles: List = mutableListOf()) =
56 | CodeCompiler.compile(
57 | pomData = pomData,
58 | outputDirPath = outputDirPath,
59 | files = mutableListOf().also { forEach { e -> it.add(e.toJavaFileObject()) } },
60 | compileOnlyFiles = mutableListOf().also { compileOnlyFiles.forEach { e -> it.add(e.toJavaFileObject()) } }
61 | )
62 |
63 | /**
64 | * 编译 [JavaFileObject] 为 Maven 依赖
65 | * @param pomData Maven POM 实体
66 | * @param outputDirPath 编译输出目录路径
67 | * @param compileOnlyFiles [JavaFileObject] 仅编译数组 - 默认空
68 | * @throws IllegalStateException 如果编译失败
69 | */
70 | @JvmName("compileWithJavaFileObject")
71 | internal fun JavaFileObject.compile(pomData: MavenPomData, outputDirPath: String, compileOnlyFiles: List = mutableListOf()) =
72 | CodeCompiler.compile(pomData, outputDirPath, listOf(this), compileOnlyFiles)
73 |
74 | /**
75 | * 编译 [JavaFileObject] 为 Maven 依赖
76 | * @param pomData Maven POM 实体
77 | * @param outputDirPath 编译输出目录路径
78 | * @param compileOnlyFiles [JavaFileObject] 仅编译数组 - 默认空
79 | * @throws IllegalStateException 如果编译失败
80 | */
81 | @JvmName("compileWithJavaFileObject")
82 | internal fun List.compile(pomData: MavenPomData, outputDirPath: String, compileOnlyFiles: List = mutableListOf()) =
83 | CodeCompiler.compile(pomData, outputDirPath, files = this, compileOnlyFiles)
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/utils/debug/SError.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | package com.highcapable.sweetproperty.utils.debug
23 |
24 | import com.highcapable.sweetproperty.SweetProperty
25 |
26 | /**
27 | * 全局异常管理类
28 | */
29 | internal object SError {
30 |
31 | /**
32 | * 抛出异常
33 | * @param msg 消息内容
34 | * @throws IllegalStateException
35 | */
36 | internal fun make(msg: String): Nothing = error("[${SweetProperty.TAG}] $msg")
37 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/com/highcapable/sweetproperty/utils/debug/SLog.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | @file:Suppress("unused", "MemberVisibilityCanBePrivate")
23 |
24 | package com.highcapable.sweetproperty.utils.debug
25 |
26 | import com.highcapable.sweetproperty.SweetProperty
27 | import org.apache.log4j.Logger
28 |
29 | /**
30 | * 全局 Log 管理类
31 | */
32 | internal object SLog {
33 |
34 | internal const val DONE = "✅"
35 | internal const val IGNORE = "❎"
36 | internal const val ERROR = "❌"
37 | internal const val WARN = "⚠️"
38 | internal const val LINK = "➡️"
39 | internal const val WIRE = "⚙️"
40 | internal const val UP = "⬆️"
41 | internal const val ROTATE = "\uD83D\uDD04"
42 | internal const val ANLZE = "\uD83D\uDD0D"
43 | internal const val STRNG = "\uD83D\uDCAA"
44 |
45 | /** 当前日志输出对象 */
46 | private val logger = Logger.getLogger(SLog::class.java)
47 |
48 | /**
49 | * 打印 Info (提醒) 级别 Log (绿色)
50 | * @param msg 消息内容
51 | * @param symbol 前缀符号 - 仅限非 [noTag] - 默认无
52 | * @param noTag 无标签 - 默认否
53 | */
54 | internal fun note(msg: Any, symbol: String = "", noTag: Boolean = false) =
55 | log(if (noTag) msg else msg.createSymbolMsg(symbol), color = "38;5;10")
56 |
57 | /**
58 | * 打印 Info 级别 Log (无颜色)
59 | * @param msg 消息内容
60 | * @param symbol 前缀符号 - 仅限非 [noTag] - 默认无
61 | * @param noTag 无标签 - 默认否
62 | */
63 | internal fun info(msg: Any, symbol: String = "", noTag: Boolean = false) =
64 | log(if (noTag) msg else msg.createSymbolMsg(symbol))
65 |
66 | /**
67 | * 打印 Warn 级别 Log (黄色)
68 | * @param msg 消息内容
69 | * @param symbol 前缀符号 - 仅限非 [noTag] - 默认 [WARN]
70 | * @param noTag 无标签 - 默认否
71 | */
72 | internal fun warn(msg: Any, symbol: String = WARN, noTag: Boolean = false) =
73 | log(if (noTag) msg else msg.createSymbolMsg(symbol), color = "33")
74 |
75 | /**
76 | * 打印 Error 级别 Log (红色)
77 | * @param msg 消息内容
78 | * @param symbol 前缀符号 - 仅限非 [noTag] - 默认 [ERROR]
79 | * @param noTag 无标签 - 默认否
80 | */
81 | internal fun error(msg: Any, symbol: String = ERROR, noTag: Boolean = false) =
82 | log(if (noTag) msg else msg.createSymbolMsg(symbol), isError = true)
83 |
84 | /**
85 | * 创建符号消息内容
86 | * @param symbol 前缀符号
87 | * @return [String]
88 | */
89 | private fun Any.createSymbolMsg(symbol: String) =
90 | if (symbol.isNotBlank()) "[${SweetProperty.TAG}] $symbol $this" else "[${SweetProperty.TAG}] $this"
91 |
92 | /**
93 | * 打印 Log
94 | * @param msg 消息内容
95 | * @param color 颜色代码 - 默认无颜色
96 | * @param isError 是否强制为错误日志 - 默认否
97 | */
98 | private fun log(msg: Any, color: String = "0", isError: Boolean = false) = when {
99 | isError -> logger.error(msg)
100 | color != "0" -> println("\u001B[${color}m$msg\u001B[0m")
101 | else -> println(msg)
102 | }
103 | }
--------------------------------------------------------------------------------
/sweetproperty-gradle-plugin/src/main/java/org/gradle/kotlin/dsl/SweetPropertySettingsExtensionFactory.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * SweetProperty - An easy get project properties anywhere Gradle plugin.
3 | * Copyright (C) 2019 HighCapable
4 | * https://github.com/HighCapable/SweetProperty
5 | *
6 | * Apache License Version 2.0
7 | *
8 | * Licensed under the Apache License, Version 2.0 (the "License");
9 | * you may not use this file except in compliance with the License.
10 | * You may obtain a copy of the License at
11 | *
12 | * https://www.apache.org/licenses/LICENSE-2.0
13 | *
14 | * Unless required by applicable law or agreed to in writing, software
15 | * distributed under the License is distributed on an "AS IS" BASIS,
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | * See the License for the specific language governing permissions and
18 | * limitations under the License.
19 | *
20 | * This file is created by fankes on 2023/8/27.
21 | */
22 | @file:Suppress("unused")
23 |
24 | package org.gradle.kotlin.dsl
25 |
26 | import com.highcapable.sweetproperty.gradle.factory.configure
27 | import com.highcapable.sweetproperty.gradle.factory.get
28 | import com.highcapable.sweetproperty.plugin.extension.dsl.configure.SweetPropertyConfigureExtension
29 | import org.gradle.api.Action
30 | import org.gradle.api.initialization.Settings
31 |
32 | /**
33 | * WORKAROUND: for some reason a type-safe accessor is not generated for the extension,
34 | * even though it is present in the extension container where the plugin is applied.
35 | * This seems to work fine, and the extension methods are only available when the plugin
36 | * is actually applied.
37 | *
38 | * See related link [here](https://stackoverflow.com/questions/72627792/gradle-settings-plugin-extension)
39 | */
40 |
41 | /**
42 | * Retrieves the [SweetPropertyConfigureExtension] extension.
43 | * @return [SweetPropertyConfigureExtension]
44 | */
45 | val Settings.sweetProperty get() = get(SweetPropertyConfigureExtension.NAME)
46 |
47 | /**
48 | * Configures the [SweetPropertyConfigureExtension] extension.
49 | * @param configure
50 | */
51 | fun Settings.sweetProperty(configure: Action) = configure(SweetPropertyConfigureExtension.NAME, configure)
--------------------------------------------------------------------------------