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 (e) { 36 return null; 37 } 38} 39 40export async function generateAndroidBuildConstantsFromMacrosAsync(macros) { 41 let source; 42 43 // android falls back to published dev home if local dev home 44 // doesn't exist or had an error. 45 const isLocalManifestEmpty = 46 !macros.BUILD_MACHINE_KERNEL_MANIFEST || macros.BUILD_MACHINE_KERNEL_MANIFEST === ''; 47 48 let versionUsed = 'local'; 49 if (process.env.USE_DOGFOODING_PUBLISHED_KERNEL_MANIFEST) { 50 macros.BUILD_MACHINE_KERNEL_MANIFEST = macros.DOGFOODING_PUBLISHED_KERNEL_MANIFEST; 51 versionUsed = 'doogfooding'; 52 } else if (isLocalManifestEmpty) { 53 macros.BUILD_MACHINE_KERNEL_MANIFEST = macros.DEV_PUBLISHED_KERNEL_MANIFEST; 54 versionUsed = 'published dev'; 55 } 56 console.log(`Using ${chalk.yellow(versionUsed)} version of Expo Home.`); 57 58 delete macros['DEV_PUBLISHED_KERNEL_MANIFEST']; 59 delete macros['DOGFOODING_PUBLISHED_KERNEL_MANIFEST']; 60 61 const definitions = Object.entries(macros).map( 62 ([name, value]) => 63 ` public static final ${formatJavaType(value)} ${name} = ${formatJavaLiteral(value)};` 64 ); 65 66 source = ` 67package host.exp.exponent.generated; 68 69public class ExponentBuildConstants { 70${definitions.join('\n')} 71}`; 72 73 return ( 74 ` 75// Copyright 2016-present 650 Industries. All rights reserved. 76// @generated by \`expotools android-generate-dynamic-macros\` 77 78${source.trim()} 79`.trim() + '\n' 80 ); 81} 82 83async function updateBuildConstants(buildConstantsPath, macros) { 84 console.log( 85 'Generating build config %s ...', 86 chalk.cyan(path.relative(EXPO_DIR, buildConstantsPath)) 87 ); 88 89 const [source, existingSource] = await Promise.all([ 90 generateAndroidBuildConstantsFromMacrosAsync(macros), 91 readExistingSourceAsync(path.resolve(buildConstantsPath)), 92 ]); 93 94 if (source !== existingSource) { 95 await fs.ensureDir(path.dirname(buildConstantsPath)); 96 await fs.writeFile(buildConstantsPath, source, 'utf8'); 97 } 98} 99 100export default class AndroidMacrosGenerator { 101 async generateAsync(options): Promise<void> { 102 const { buildConstantsPath, macros } = options; 103 104 await updateBuildConstants(path.resolve(buildConstantsPath), macros); 105 } 106 107 async cleanupAsync(options): Promise<void> { 108 // Nothing to clean on Android 109 } 110} 111