├── .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 | 14 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 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 | [![GitHub license](https://img.shields.io/github/license/HighCapable/SweetProperty?color=blue)](https://github.com/HighCapable/SweetProperty/blob/master/LICENSE) 4 | [![GitHub release](https://img.shields.io/github/v/release/HighCapable/SweetProperty?display_name=release&logo=github&color=green)](https://github.com/HighCapable/SweetProperty/releases) 5 | [![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev) 6 | [![QQ](https://img.shields.io/badge/discussion-QQ-blue.svg?logo=tencent-qq&logoColor=red)](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf) 7 | 8 | LOGO 9 | 10 | 一个轻松在任意地方获取项目属性的 Gradle 插件。 11 | 12 | [English](README.md) | 简体中文 13 | 14 | | LOGO | [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 | ![Star History Chart](https://api.star-history.com/svg?repos=HighCapable/SweetProperty&type=Date) 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 | [![GitHub license](https://img.shields.io/github/license/HighCapable/SweetProperty?color=blue)](https://github.com/HighCapable/SweetProperty/blob/master/LICENSE) 4 | [![GitHub release](https://img.shields.io/github/v/release/HighCapable/SweetProperty?display_name=release&logo=github&color=green)](https://github.com/HighCapable/SweetProperty/releases) 5 | [![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev) 6 | [![QQ](https://img.shields.io/badge/discussion-QQ-blue.svg?logo=tencent-qq&logoColor=red)](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf) 7 | 8 | LOGO 9 | 10 | An easy get project properties anywhere Gradle plugin. 11 | 12 | English | [简体中文](README-zh-CN.md) 13 | 14 | | LOGO | [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 |

→ To see more about my projects, please click here ←

72 |
73 | 74 | ## Star History 75 | 76 | ![Star History Chart](https://api.star-history.com/svg?repos=HighCapable/SweetProperty&type=Date) 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) --------------------------------------------------------------------------------