1 /*
<lambda>null2  * Copyright (C) 2019 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.ndkports
18 
19 import okhttp3.OkHttpClient
20 import okhttp3.Request
21 import java.io.File
22 import java.io.FileOutputStream
23 
24 @Suppress("unused")
25 fun executeProcessStep(
26     args: List<String>,
27     workingDirectory: File,
28     additionalEnvironment: Map<String, String>? = null
29 ): Result<Unit, String> {
30     val pb = ProcessBuilder(args)
31         .redirectOutput(ProcessBuilder.Redirect.INHERIT)
32         .redirectError(ProcessBuilder.Redirect.INHERIT)
33         .directory(workingDirectory)
34 
35     if (additionalEnvironment != null) {
36         pb.environment().putAll(additionalEnvironment)
37     }
38 
39     return pb.start()
40         .waitFor().let {
41             if (it == 0) {
42                 Result.Ok(Unit)
43             } else {
44                 Result.Error("Process failed with exit code $it")
45             }
46         }
47 }
48 
installDirectoryForPortnull49 fun installDirectoryForPort(
50     name: String,
51     workingDirectory: File,
52     toolchain: Toolchain
53 ): File = workingDirectory.parentFile.resolve("$name/install/${toolchain.abi}")
54 
55 data class Module(
56     val name: String,
57     val includesPerAbi: Boolean = false,
58     val dependencies: List<String> = emptyList()
59 )
60 
61 abstract class Port {
62     abstract val name: String
63     abstract val version: String
64     open val prefabVersion: CMakeCompatibleVersion
65         get() = CMakeCompatibleVersion.parse(version)
66     open val mavenVersion: String
67         get() = version
68 
69     abstract val url: String
70 
71     open val licensePath: String = "LICENSE"
72 
73     abstract val license: License
74 
75     open val dependencies: List<String> = emptyList()
76     abstract val modules: List<Module>
77 
78     protected val ncpus = Runtime.getRuntime().availableProcessors()
79 
80     fun run(
81         toolchain: Toolchain,
82         sourceDirectory: File,
83         buildDirectory: File,
84         installDirectory: File,
85         workingDirectory: File
86     ): Result<Unit, String> {
87         configure(
88             toolchain,
89             sourceDirectory,
90             buildDirectory,
91             installDirectory,
92             workingDirectory
93         ).onFailure { return Result.Error(it) }
94 
95         build(toolchain, buildDirectory).onFailure { return Result.Error(it) }
96 
97         install(
98             toolchain,
99             buildDirectory,
100             installDirectory
101         ).onFailure { return Result.Error(it) }
102 
103         return Result.Ok(Unit)
104     }
105 
106     open fun fetchSource(
107         sourceDirectory: File,
108         workingDirectory: File
109     ): Result<Unit, String> {
110         val file = workingDirectory.resolve(File(url).name)
111 
112         val client = OkHttpClient()
113         val request = Request.Builder().url(url).build()
114         client.newCall(request).execute().use { response ->
115             if (!response.isSuccessful) {
116                 return Result.Error("Failed to download $url")
117             }
118 
119             val body = response.body ?: throw RuntimeException(
120                 "Expected non-null response body for $url"
121             )
122             FileOutputStream(file).use { output ->
123                 body.byteStream().use { input ->
124                     input.copyTo(output)
125                 }
126             }
127         }
128 
129         sourceDirectory.mkdirs()
130         return executeProcessStep(
131             listOf(
132                 "tar",
133                 "xf",
134                 file.absolutePath,
135                 "--strip-components=1"
136             ), sourceDirectory
137         )
138     }
139 
140     open fun configure(
141         toolchain: Toolchain,
142         sourceDirectory: File,
143         buildDirectory: File,
144         installDirectory: File,
145         workingDirectory: File
146     ): Result<Unit, String> = Result.Ok(Unit)
147 
148     open fun build(
149         toolchain: Toolchain,
150         buildDirectory: File
151     ): Result<Unit, String> = Result.Ok(Unit)
152 
153     open fun install(
154         toolchain: Toolchain,
155         buildDirectory: File,
156         installDirectory: File
157     ): Result<Unit, String> = Result.Ok(Unit)
158 }