generate-full: Add support for flattening APEX modules
This commit is contained in:
parent
de09864601
commit
ec580e9957
2 changed files with 133 additions and 120 deletions
|
@ -1,6 +1,7 @@
|
||||||
import { Command, flags } from '@oclif/command'
|
import { Command, flags } from '@oclif/command'
|
||||||
import { promises as fs } from 'fs'
|
import { promises as fs } from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
import { flattenAllApexs } from '../blobs/apex'
|
||||||
|
|
||||||
import { createVendorDirs, generateBuild, writeBuildFiles } from '../blobs/build'
|
import { createVendorDirs, generateBuild, writeBuildFiles } from '../blobs/build'
|
||||||
import { copyBlobs } from '../blobs/copy'
|
import { copyBlobs } from '../blobs/copy'
|
||||||
|
@ -17,6 +18,7 @@ import { parseSystemState, SystemState } from '../config/system-state'
|
||||||
import { ANDROID_INFO, extractFactoryFirmware, generateAndroidInfo, writeFirmwareImages } from '../images/firmware'
|
import { ANDROID_INFO, extractFactoryFirmware, generateAndroidInfo, writeFirmwareImages } from '../images/firmware'
|
||||||
import { diffPartContexts, parseContextsRecursive, parsePartContexts, resolvePartContextDiffs, SelinuxContexts } from '../sepolicy/contexts'
|
import { diffPartContexts, parseContextsRecursive, parsePartContexts, resolvePartContextDiffs, SelinuxContexts } from '../sepolicy/contexts'
|
||||||
import { startActionSpinner, stopActionSpinner } from '../util/cli'
|
import { startActionSpinner, stopActionSpinner } from '../util/cli'
|
||||||
|
import { withTempDir } from '../util/fs'
|
||||||
import { ALL_PARTITIONS } from '../util/partitions'
|
import { ALL_PARTITIONS } from '../util/partitions'
|
||||||
|
|
||||||
export default class GenerateFull extends Command {
|
export default class GenerateFull extends Command {
|
||||||
|
@ -100,138 +102,148 @@ export default class GenerateFull extends Command {
|
||||||
})
|
})
|
||||||
stopActionSpinner(spinner)
|
stopActionSpinner(spinner)
|
||||||
|
|
||||||
// 4. Extract
|
// Create tmp dir in case we extract APEXs
|
||||||
// Copy blobs (this has its own spinner)
|
await withTempDir(async (tmp) => {
|
||||||
if (!skipCopy) {
|
// 4. Flatten APEX modules
|
||||||
await copyBlobs(entries, stockRoot, dirs.proprietary)
|
if (config.flatten_apex) {
|
||||||
}
|
spinner = startActionSpinner('Flattening APEX modules')
|
||||||
|
entries = await flattenAllApexs(entries, stockRoot, tmp)
|
||||||
// 5. Props
|
stopActionSpinner(spinner)
|
||||||
spinner = startActionSpinner('Extracting properties')
|
|
||||||
let stockProps = await loadPartitionProps(stockRoot)
|
|
||||||
let customProps = customState?.partitionProps ?? await loadPartitionProps(customRoot)
|
|
||||||
// Filter props
|
|
||||||
if (config.prop_filters != null) {
|
|
||||||
filterPartPropKeys(stockProps, config.prop_filters)
|
|
||||||
filterPartPropKeys(customProps, config.prop_filters)
|
|
||||||
}
|
|
||||||
// Diff
|
|
||||||
let propChanges = diffPartitionProps(stockProps, customProps)
|
|
||||||
let missingProps = new Map(Array.from(propChanges.entries())
|
|
||||||
.map(([part, props]) => [part, props.removed]))
|
|
||||||
// Fingerprint for SafetyNet
|
|
||||||
let fingerprint = stockProps.get('system')!.get('ro.system.build.fingerprint')!
|
|
||||||
// A/B OTA partitions
|
|
||||||
let stockOtaParts = stockProps.get('product')!.get('ro.product.ab_ota_partitions')!.split(',')
|
|
||||||
let customOtaParts = new Set(customProps.get('product')!.get('ro.product.ab_ota_partitions')!.split(','))
|
|
||||||
let missingOtaParts = stockOtaParts.filter(p => !customOtaParts.has(p))
|
|
||||||
stopActionSpinner(spinner)
|
|
||||||
|
|
||||||
// 6. SELinux policies
|
|
||||||
spinner = startActionSpinner('Adding missing SELinux policies')
|
|
||||||
// Built contexts
|
|
||||||
let stockContexts = await parsePartContexts(stockRoot)
|
|
||||||
let customContexts = customState?.partitionSecontexts ?? await parsePartContexts(customRoot)
|
|
||||||
// Contexts from AOSP
|
|
||||||
let sourceContexts: SelinuxContexts = new Map<string, string>()
|
|
||||||
for (let dir of config.sepolicy_dirs) {
|
|
||||||
// TODO: support alternate ROM root
|
|
||||||
let contexts = await parseContextsRecursive(dir, '.')
|
|
||||||
for (let [ctx, source] of contexts.entries()) {
|
|
||||||
sourceContexts.set(ctx, source)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Diff; reversed custom->stock order to get *missing* contexts
|
|
||||||
let ctxDiffs = diffPartContexts(customContexts, stockContexts)
|
|
||||||
let ctxResolutions = resolvePartContextDiffs(ctxDiffs, sourceContexts)
|
|
||||||
stopActionSpinner(spinner)
|
|
||||||
|
|
||||||
// 7. Overlays
|
// 5. Extract
|
||||||
spinner = startActionSpinner('Extracting overlays')
|
// Copy blobs (this has its own spinner)
|
||||||
let stockOverlays = await parsePartOverlayApks(aapt2Path, stockRoot, path => {
|
if (!skipCopy) {
|
||||||
spinner.text = path
|
await copyBlobs(entries, stockRoot, dirs.proprietary)
|
||||||
})
|
}
|
||||||
let customOverlays = customState?.partitionOverlays ??
|
|
||||||
await parsePartOverlayApks(aapt2Path, customRoot, path => {
|
// 6. Props
|
||||||
|
spinner = startActionSpinner('Extracting properties')
|
||||||
|
let stockProps = await loadPartitionProps(stockRoot)
|
||||||
|
let customProps = customState?.partitionProps ?? await loadPartitionProps(customRoot)
|
||||||
|
// Filter props
|
||||||
|
if (config.prop_filters != null) {
|
||||||
|
filterPartPropKeys(stockProps, config.prop_filters)
|
||||||
|
filterPartPropKeys(customProps, config.prop_filters)
|
||||||
|
}
|
||||||
|
// Diff
|
||||||
|
let propChanges = diffPartitionProps(stockProps, customProps)
|
||||||
|
let missingProps = new Map(Array.from(propChanges.entries())
|
||||||
|
.map(([part, props]) => [part, props.removed]))
|
||||||
|
// Fingerprint for SafetyNet
|
||||||
|
let fingerprint = stockProps.get('system')!.get('ro.system.build.fingerprint')!
|
||||||
|
// A/B OTA partitions
|
||||||
|
let stockOtaParts = stockProps.get('product')!.get('ro.product.ab_ota_partitions')!.split(',')
|
||||||
|
let customOtaParts = new Set(customProps.get('product')!.get('ro.product.ab_ota_partitions')!.split(','))
|
||||||
|
let missingOtaParts = stockOtaParts.filter(p => !customOtaParts.has(p))
|
||||||
|
stopActionSpinner(spinner)
|
||||||
|
|
||||||
|
// 7. SELinux policies
|
||||||
|
spinner = startActionSpinner('Adding missing SELinux policies')
|
||||||
|
// Built contexts
|
||||||
|
let stockContexts = await parsePartContexts(stockRoot)
|
||||||
|
let customContexts = customState?.partitionSecontexts ?? await parsePartContexts(customRoot)
|
||||||
|
// Contexts from AOSP
|
||||||
|
let sourceContexts: SelinuxContexts = new Map<string, string>()
|
||||||
|
for (let dir of config.sepolicy_dirs) {
|
||||||
|
// TODO: support alternate ROM root
|
||||||
|
let contexts = await parseContextsRecursive(dir, '.')
|
||||||
|
for (let [ctx, source] of contexts.entries()) {
|
||||||
|
sourceContexts.set(ctx, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Diff; reversed custom->stock order to get *missing* contexts
|
||||||
|
let ctxDiffs = diffPartContexts(customContexts, stockContexts)
|
||||||
|
let ctxResolutions = resolvePartContextDiffs(ctxDiffs, sourceContexts)
|
||||||
|
stopActionSpinner(spinner)
|
||||||
|
|
||||||
|
// 8. Overlays
|
||||||
|
spinner = startActionSpinner('Extracting overlays')
|
||||||
|
let stockOverlays = await parsePartOverlayApks(aapt2Path, stockRoot, path => {
|
||||||
spinner.text = path
|
spinner.text = path
|
||||||
})
|
})
|
||||||
let missingOverlays = diffPartOverlays(stockOverlays, customOverlays)
|
let customOverlays = customState?.partitionOverlays ??
|
||||||
let overlayPkgs = await serializePartOverlays(missingOverlays, dirs.overlays)
|
await parsePartOverlayApks(aapt2Path, customRoot, path => {
|
||||||
stopActionSpinner(spinner)
|
spinner.text = path
|
||||||
|
})
|
||||||
// 8. vintf manifests
|
let missingOverlays = diffPartOverlays(stockOverlays, customOverlays)
|
||||||
spinner = startActionSpinner('Extracting vintf manifests')
|
let overlayPkgs = await serializePartOverlays(missingOverlays, dirs.overlays)
|
||||||
let customVintf = customState?.partitionVintfInfo ?? await loadPartVintfInfo(customRoot)
|
|
||||||
let stockVintf = await loadPartVintfInfo(stockRoot)
|
|
||||||
let missingHals = diffPartVintfManifests(customVintf, stockVintf)
|
|
||||||
let vintfManifestPaths = await writePartVintfManifests(missingHals, dirs.vintf)
|
|
||||||
stopActionSpinner(spinner)
|
|
||||||
|
|
||||||
// 9. Firmware
|
|
||||||
let fwPaths: Array<string> | null = null
|
|
||||||
if (factoryZip != undefined) {
|
|
||||||
spinner = startActionSpinner('Extracting firmware')
|
|
||||||
let fwImages = await extractFactoryFirmware(factoryZip)
|
|
||||||
fwPaths = await writeFirmwareImages(fwImages, dirs.firmware)
|
|
||||||
|
|
||||||
// Generate android-info.txt from device and versions
|
|
||||||
let androidInfo = generateAndroidInfo(
|
|
||||||
config.device.name,
|
|
||||||
stockProps.get('vendor')!.get('ro.build.expect.bootloader')!,
|
|
||||||
stockProps.get('vendor')!.get('ro.build.expect.baseband')!,
|
|
||||||
)
|
|
||||||
await fs.writeFile(`${dirs.firmware}/${ANDROID_INFO}`, androidInfo)
|
|
||||||
|
|
||||||
stopActionSpinner(spinner)
|
stopActionSpinner(spinner)
|
||||||
}
|
|
||||||
|
|
||||||
// 10. Build files
|
// 9. vintf manifests
|
||||||
spinner = startActionSpinner('Generating build files')
|
spinner = startActionSpinner('Extracting vintf manifests')
|
||||||
let build = await generateBuild(entries, config.device.name, config.device.vendor, stockRoot, dirs)
|
let customVintf = customState?.partitionVintfInfo ?? await loadPartVintfInfo(customRoot)
|
||||||
|
let stockVintf = await loadPartVintfInfo(stockRoot)
|
||||||
|
let missingHals = diffPartVintfManifests(customVintf, stockVintf)
|
||||||
|
let vintfManifestPaths = await writePartVintfManifests(missingHals, dirs.vintf)
|
||||||
|
stopActionSpinner(spinner)
|
||||||
|
|
||||||
// Add rules to build overridden modules and overlays, then re-sort
|
// 10. Firmware
|
||||||
build.deviceMakefile!.packages!.push(...builtModules, ...overlayPkgs)
|
let fwPaths: Array<string> | null = null
|
||||||
build.deviceMakefile!.packages!.sort((a, b) => a.localeCompare(b))
|
if (factoryZip != undefined) {
|
||||||
|
spinner = startActionSpinner('Extracting firmware')
|
||||||
|
let fwImages = await extractFactoryFirmware(factoryZip)
|
||||||
|
fwPaths = await writeFirmwareImages(fwImages, dirs.firmware)
|
||||||
|
|
||||||
// Add device parts
|
// Generate android-info.txt from device and versions
|
||||||
build.deviceMakefile = {
|
let androidInfo = generateAndroidInfo(
|
||||||
props: missingProps,
|
config.device.name,
|
||||||
fingerprint: fingerprint,
|
stockProps.get('vendor')!.get('ro.build.expect.bootloader')!,
|
||||||
vintfManifestPaths: vintfManifestPaths,
|
stockProps.get('vendor')!.get('ro.build.expect.baseband')!,
|
||||||
...build.deviceMakefile,
|
)
|
||||||
}
|
await fs.writeFile(`${dirs.firmware}/${ANDROID_INFO}`, androidInfo)
|
||||||
|
|
||||||
// Add board parts
|
stopActionSpinner(spinner)
|
||||||
build.boardMakefile = {
|
}
|
||||||
secontextResolutions: ctxResolutions,
|
|
||||||
...(missingOtaParts.length > 0 && { abOtaPartitions: missingOtaParts }),
|
|
||||||
...(fwPaths != null && { boardInfo: `${dirs.firmware}/${ANDROID_INFO}` }),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add firmware
|
// 11. Build files
|
||||||
if (fwPaths != null) {
|
spinner = startActionSpinner('Generating build files')
|
||||||
build.modulesMakefile!.radioFiles = fwPaths.map(p => path.relative(dirs.out, p))
|
let build = await generateBuild(entries, config.device.name, config.device.vendor, stockRoot, dirs)
|
||||||
}
|
|
||||||
|
|
||||||
// Create device
|
// Add rules to build overridden modules and overlays, then re-sort
|
||||||
let productProps = stockProps.get('product')!
|
build.deviceMakefile!.packages!.push(...builtModules, ...overlayPkgs)
|
||||||
let productName = productProps.get('ro.product.product.name')!
|
build.deviceMakefile!.packages!.sort((a, b) => a.localeCompare(b))
|
||||||
build.productMakefile = {
|
|
||||||
baseProductPath: config.product_makefile,
|
|
||||||
name: productName,
|
|
||||||
model: productProps.get('ro.product.product.model')!,
|
|
||||||
brand: productProps.get('ro.product.product.brand')!,
|
|
||||||
manufacturer: productProps.get('ro.product.product.manufacturer')!,
|
|
||||||
}
|
|
||||||
build.productsMakefile = {
|
|
||||||
products: [productName],
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump list
|
// Add device parts
|
||||||
let fileList = serializeBlobList(entries)
|
build.deviceMakefile = {
|
||||||
await fs.writeFile(`${dirs.out}/proprietary-files.txt`, fileList)
|
props: missingProps,
|
||||||
|
fingerprint: fingerprint,
|
||||||
|
vintfManifestPaths: vintfManifestPaths,
|
||||||
|
...build.deviceMakefile,
|
||||||
|
}
|
||||||
|
|
||||||
await writeBuildFiles(build, dirs)
|
// Add board parts
|
||||||
stopActionSpinner(spinner)
|
build.boardMakefile = {
|
||||||
|
secontextResolutions: ctxResolutions,
|
||||||
|
...(missingOtaParts.length > 0 && { abOtaPartitions: missingOtaParts }),
|
||||||
|
...(fwPaths != null && { boardInfo: `${dirs.firmware}/${ANDROID_INFO}` }),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add firmware
|
||||||
|
if (fwPaths != null) {
|
||||||
|
build.modulesMakefile!.radioFiles = fwPaths.map(p => path.relative(dirs.out, p))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create device
|
||||||
|
let productProps = stockProps.get('product')!
|
||||||
|
let productName = productProps.get('ro.product.product.name')!
|
||||||
|
build.productMakefile = {
|
||||||
|
baseProductPath: config.product_makefile,
|
||||||
|
name: productName,
|
||||||
|
model: productProps.get('ro.product.product.model')!,
|
||||||
|
brand: productProps.get('ro.product.product.brand')!,
|
||||||
|
manufacturer: productProps.get('ro.product.product.manufacturer')!,
|
||||||
|
}
|
||||||
|
build.productsMakefile = {
|
||||||
|
products: [productName],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump list
|
||||||
|
let fileList = serializeBlobList(entries)
|
||||||
|
await fs.writeFile(`${dirs.out}/proprietary-files.txt`, fileList)
|
||||||
|
|
||||||
|
await writeBuildFiles(build, dirs)
|
||||||
|
stopActionSpinner(spinner)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ export interface DeviceConfig {
|
||||||
namespaces?: Array<string>
|
namespaces?: Array<string>
|
||||||
sepolicy_dirs: Array<string>
|
sepolicy_dirs: Array<string>
|
||||||
product_makefile: string
|
product_makefile: string
|
||||||
|
flatten_apex?: boolean
|
||||||
|
|
||||||
prop_filters?: PropFilters
|
prop_filters?: PropFilters
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue