1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.metalava.cli.signature 18 19 import com.android.tools.metalava.cli.common.existingFile 20 import com.android.tools.metalava.cli.common.map 21 import com.android.tools.metalava.model.text.FileFormat 22 import com.github.ajalt.clikt.parameters.groups.OptionGroup 23 import com.github.ajalt.clikt.parameters.options.convert 24 import com.github.ajalt.clikt.parameters.options.default 25 import com.github.ajalt.clikt.parameters.options.option 26 27 const val ARG_FORMAT = "--format" 28 const val ARG_USE_SAME_FORMAT_AS = "--use-same-format-as" 29 30 /** The name of the group, can be used in help text to refer to the options in this group. */ 31 const val SIGNATURE_FORMAT_OUTPUT_GROUP = "Signature Format Output" 32 33 private val versionToFileFormat = 34 mapOf( 35 "v2" to FileFormat.V2, 36 "v3" to FileFormat.V3, 37 "v4" to FileFormat.V4, 38 "latest" to FileFormat.LATEST, 39 "recommended" to FileFormat.V2, 40 ) 41 42 class SignatureFormatOptions( 43 /** If true then the `migrating` property is allowed, otherwise it is not allowed at all. */ 44 private val migratingAllowed: Boolean = false, 45 ) : 46 OptionGroup( 47 name = SIGNATURE_FORMAT_OUTPUT_GROUP, 48 help = 49 """ 50 Options controlling the format of the generated signature files. 51 52 See `metalava help signature-file-formats` for more information. 53 """ 54 .trimIndent() 55 ) { 56 57 private val formatDefaults by 58 option( 59 "--format-defaults", 60 metavar = "<defaults>", 61 help = 62 """ 63 Specifies defaults for format properties. 64 65 A comma separated list of `<property>=<value>` assignments where 66 `<property>` is one of the following: 67 '${FileFormat.defaultableProperties().joinToString(separator = "', '")}'. 68 69 See `metalava help signature-file-formats` for more information on the 70 properties. 71 """ 72 .trimIndent(), 73 ) 74 .convert { defaults -> FileFormat.parseDefaults(defaults) } 75 76 /** The output format version being used */ 77 private val formatSpecifier by 78 option( 79 ARG_FORMAT, 80 metavar = "[v2|v3|v4|latest|recommended|<specifier>]", 81 help = 82 """ 83 Specifies the output signature file format. 84 85 The preferred way of specifying the format is to use one of the following 86 values (in no particular order): 87 88 latest - The latest in the supported versions. Only use this if you want to 89 have the very latest and are prepared to update signature files on a 90 continuous basis. 91 92 recommended (default) - The recommended version to use. This is currently 93 set to `v2` and will only change very infrequently so can be considered 94 stable. 95 96 <specifier> - which has the following syntax: 97 ``` 98 <version>[:<property>=<value>[,<property>=<value>]*] 99 ``` 100 101 Where: 102 103 104 The following values are still supported but should be considered 105 deprecated. 106 107 v2 - The main version used in Android. 108 109 v3 - Adds support for using kotlin style syntax to embed nullability 110 information instead of using explicit and verbose @NonNull and @Nullable 111 annotations. This can be used for Java files and Kotlin files alike. 112 113 v4 - Adds support for using concise default values in parameters. Instead 114 of specifying the actual default values it just uses the `default` keyword. 115 """ 116 .trimIndent(), 117 ) 118 .convert { specifier -> 119 versionToFileFormat[specifier] 120 ?: FileFormat.parseSpecifier( 121 specifier = specifier, 122 migratingAllowed = migratingAllowed, 123 extraVersions = versionToFileFormat.keys, 124 ) 125 } 126 .default(FileFormat.V2, defaultForHelp = "recommended") 127 128 private val useSameFormatAs by 129 option( 130 ARG_USE_SAME_FORMAT_AS, 131 help = 132 """ 133 Specifies that the output format should be the same as the format used in 134 the specified file. It is an error if the file does not exist. If the file 135 is empty then this will behave as if it was not specified. If the file is 136 not a valid signature file then it will fail. Otherwise, the format read 137 from the file will be used. 138 139 If this is specified (and the file is not empty) then this will be used in 140 preference to most of the other options in this group. Those options will be 141 validated but otherwise ignored. 142 143 The intention is that the other options will be used to specify the default 144 for new empty API files (e.g. created using `touch`) while this option is 145 used to specify the format for generating updates to the existing non-empty 146 files. 147 """ 148 .trimIndent(), 149 ) 150 .existingFile() 151 .map { file -> 152 file?.reader(Charsets.UTF_8)?.use { FileFormat.parseHeader(file.toPath(), it) } 153 } 154 155 /** 156 * The [FileFormat] produced by merging all the format related options into one cohesive set of 157 * format related properties. It combines the defaults 158 */ 159 val fileFormat: FileFormat by 160 lazy(LazyThreadSafetyMode.NONE) { 161 // Check the useSameFormatAs first and if it is not specified (or is an empty file) then 162 // fall back to the other options. 163 val format = useSameFormatAs ?: formatSpecifier 164 165 // Apply any additional overrides. 166 formatDefaults?.let { format.copy(formatDefaults = it) } ?: format 167 } 168 } 169