1 /*
2  * 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.OptionsDelegate
20 import com.android.tools.metalava.SignatureWriter
21 import com.android.tools.metalava.cli.common.MetalavaCliException
22 import com.android.tools.metalava.cli.common.MetalavaSubCommand
23 import com.android.tools.metalava.cli.common.existingFile
24 import com.android.tools.metalava.cli.common.newFile
25 import com.android.tools.metalava.cli.common.progressTracker
26 import com.android.tools.metalava.createReportFile
27 import com.android.tools.metalava.model.text.ApiFile
28 import com.android.tools.metalava.model.text.ApiParseException
29 import com.android.tools.metalava.model.text.SignatureFile
30 import com.android.tools.metalava.model.visitors.ApiVisitor
31 import com.github.ajalt.clikt.parameters.arguments.argument
32 import com.github.ajalt.clikt.parameters.arguments.multiple
33 import com.github.ajalt.clikt.parameters.groups.provideDelegate
34 import com.github.ajalt.clikt.parameters.options.option
35 import com.github.ajalt.clikt.parameters.options.required
36 
37 class MergeSignaturesCommand :
38     MetalavaSubCommand(
39         help =
40             """
41                 Merge multiple signature files together into a single file.
42 
43                 The files must all be from the same API surface. The input files may overlap at the
44                 package and class level but if two files do include the same class they must be
45                 identical. Note: It is the user's responsibility to ensure that these constraints
46                 are met as metalava does not have the information available to enforce it. Failure
47                 to do so will result in undefined behavior.
48             """
49                 .trimIndent(),
50     ) {
51     /** Add options for controlling the format of the generated files. */
52     private val signatureFormat by SignatureFormatOptions()
53 
54     private val files by
55         argument(
56                 name = "<files>",
57                 help =
58                     """
59                         Multiple signature files that will be merged together.
60                     """
61                         .trimIndent()
62             )
63             .existingFile()
64             .multiple(required = true)
65 
66     private val out by
67         option(
68                 "--out",
69                 help =
70                     """
71                         The output file into which the result will be written. The format of the
72                         file will be determined by the options in `$SIGNATURE_FORMAT_OUTPUT_GROUP`.
73                     """
74                         .trimIndent()
75             )
76             .newFile()
77             .required()
78 
runnull79     override fun run() {
80         // Make sure that none of the code called by this command accesses the global `options`
81         // property.
82         OptionsDelegate.disallowAccess()
83 
84         try {
85             val codebase = ApiFile.parseApi(SignatureFile.fromFiles(files))
86             createReportFile(progressTracker, codebase, out, description = "Merged file") {
87                 SignatureWriter(
88                     writer = it,
89                     filterEmit = { true },
90                     filterReference = { true },
91                     preFiltered = true,
92                     fileFormat = signatureFormat.fileFormat,
93                     showUnannotated = false,
94                     apiVisitorConfig = ApiVisitor.Config(),
95                 )
96             }
97         } catch (e: ApiParseException) {
98             throw MetalavaCliException(stderr = e.message)
99         }
100     }
101 }
102