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