1import chalk from 'chalk'; 2import fs from 'fs-extra'; 3import path from 'path'; 4 5import * as Directories from '../Directories'; 6 7const EXPO_DIR = Directories.getExpoRepositoryRootDir(); 8 9function formatJavaType(value) { 10 if (value == null) { 11 return 'String'; 12 } else if (typeof value === 'string') { 13 return 'String'; 14 } else if (typeof value === 'number') { 15 return 'int'; 16 } 17 throw new Error(`Unsupported literal value: ${value}`); 18} 19 20function formatJavaLiteral(value) { 21 if (value == null) { 22 return 'null'; 23 } else if (typeof value === 'string') { 24 value = value.replace(/"/g, '\\"'); 25 return `"${value}"`; 26 } else if (typeof value === 'number') { 27 return value; 28 } 29 throw new Error(`Unsupported literal value: ${value}`); 30} 31 32async function readExistingSourceAsync(filepath) { 33 try { 34 return await fs.readFile(filepath, 'utf8'); 35 } catch { 36 return null; 37 } 38} 39 40export async function generateAndroidBuildConstantsFromMacrosAsync(macros) { 41 // android falls back to published dev home if local dev home 42 // doesn't exist or had an error. 43 const isLocalManifestEmpty = 44 !macros.BUILD_MACHINE_KERNEL_MANIFEST || macros.BUILD_MACHINE_KERNEL_MANIFEST === ''; 45 46 let versionUsed = 'local'; 47 if (isLocalManifestEmpty) { 48 macros.BUILD_MACHINE_KERNEL_MANIFEST = macros.DEV_PUBLISHED_KERNEL_MANIFEST; 49 versionUsed = 'published dev'; 50 } 51 console.log(`Using ${chalk.yellow(versionUsed)} version of Expo Home.`); 52 53 delete macros['DEV_PUBLISHED_KERNEL_MANIFEST']; 54 55 const definitions = Object.entries(macros).map( 56 ([name, value]) => 57 ` public static final ${formatJavaType(value)} ${name} = ${formatJavaLiteral(value)};` 58 ); 59 60 const source = ` 61package host.exp.exponent.generated; 62 63public class ExponentBuildConstants { 64${definitions.join('\n')} 65}`; 66 67 return ( 68 ` 69// Copyright 2016-present 650 Industries. All rights reserved. 70// @generated by \`expotools android-generate-dynamic-macros\` 71 72${source.trim()} 73`.trim() + '\n' 74 ); 75} 76 77async function updateBuildConstants(buildConstantsPath, macros) { 78 console.log( 79 'Generating build config %s ...', 80 chalk.cyan(path.relative(EXPO_DIR, buildConstantsPath)) 81 ); 82 83 const [source, existingSource] = await Promise.all([ 84 generateAndroidBuildConstantsFromMacrosAsync(macros), 85 readExistingSourceAsync(path.resolve(buildConstantsPath)), 86 ]); 87 88 if (source !== existingSource) { 89 await fs.ensureDir(path.dirname(buildConstantsPath)); 90 await fs.writeFile(buildConstantsPath, source, 'utf8'); 91 } 92} 93 94export default class AndroidMacrosGenerator { 95 async generateAsync(options): Promise<void> { 96 const { buildConstantsPath, macros } = options; 97 98 await updateBuildConstants(path.resolve(buildConstantsPath), macros); 99 } 100 101 async cleanupAsync(options): Promise<void> { 102 // Nothing to clean on Android 103 } 104} 105