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 = buildMap { 34 // For any FileFormat version that has a legacy command line alias add a mapping from that alias 35 // to the FileFormat defaults appropriate for that version. 36 FileFormat.Version.entries 37 .filter { it.legacyCommandLineAlias != null } 38 .associateByTo(this, { it.legacyCommandLineAlias!! }) { it.defaults } 39 put("latest", FileFormat.LATEST) 40 put("recommended", FileFormat.V2) 41 } 42 43 class SignatureFormatOptions( 44 /** If true then the `migrating` property is allowed, otherwise it is not allowed at all. */ 45 private val migratingAllowed: Boolean = false, 46 ) : 47 OptionGroup( 48 name = SIGNATURE_FORMAT_OUTPUT_GROUP, 49 help = 50 """ 51 Options controlling the format of the generated signature files. 52 53 See `metalava help signature-file-formats` for more information. 54 """ 55 .trimIndent() 56 ) { 57 58 private val formatDefaults by 59 option( 60 "--format-defaults", 61 metavar = "<defaults>", 62 help = 63 """ 64 Specifies defaults for format properties. 65 66 A comma separated list of `<property>=<value>` assignments where 67 `<property>` is one of the following: 68 '${FileFormat.defaultableProperties().joinToString(separator = "', '")}'. 69 70 See `metalava help signature-file-formats` for more information on the 71 properties. 72 """ 73 .trimIndent(), 74 ) defaultsnull75 .convert { defaults -> FileFormat.parseDefaults(defaults) } 76 77 /** The output format version being used */ 78 private val formatSpecifier by 79 option( 80 ARG_FORMAT, 81 metavar = "[v2|v4|latest|recommended|<specifier>]", 82 help = 83 """ 84 Specifies the output signature file format. 85 86 The preferred way of specifying the format is to use one of the following 87 values (in no particular order): 88 89 latest - The latest in the supported versions. Only use this if you want to 90 have the very latest and are prepared to update signature files on a 91 continuous basis. 92 93 recommended (default) - The recommended version to use. This is currently 94 set to `v2` and will only change very infrequently so can be considered 95 stable. 96 97 <specifier> - which has the following syntax: 98 ``` 99 <version>[:<property>=<value>[,<property>=<value>]*] 100 ``` 101 102 Where: 103 104 105 The following values are still supported but should be considered 106 deprecated. 107 108 v2 - The main version used in Android. 109 110 v4 - Adds support for using kotlin style syntax to embed nullability 111 information instead of using explicit and verbose @NonNull and @Nullable 112 annotations. This can be used for Java files and Kotlin files alike. Also, 113 adds support for recording that a parameter has a default value by using the 114 pseudo-modifier `optional`. 115 """ 116 .trimIndent(), 117 ) specifiernull118 .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() filenull151 .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 <lambda>null160 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