generate-full: Add support for flattening APEX modules

This commit is contained in:
Danny Lin 2021-11-21 19:48:37 -08:00
parent de09864601
commit ec580e9957
2 changed files with 133 additions and 120 deletions

View file

@ -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)
})
} }
} }

View file

@ -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