diff --git a/android/package.v b/android/package.v index 2ae2741..8552dae 100644 --- a/android/package.v +++ b/android/package.v @@ -12,11 +12,11 @@ import vab.android.env import vab.android.sdk import vab.android.util -pub const default_app_name = 'V Test App' -pub const default_package_id = 'io.v.android' -pub const default_activity_name = 'VActivity' -pub const default_package_format = 'apk' -pub const default_min_sdk_version = 21 +pub const default_app_name = $d('vab:default_app_name', 'V Test App') +pub const default_package_id = $d('vab:default_package_id', 'io.v.android') +pub const default_activity_name = $d('vab:default_activity_name', 'VActivity') +pub const default_package_format = $d('vab:default_package_format', 'apk') +pub const default_min_sdk_version = int($d('vab:default_min_sdk_version', 21)) pub const default_base_files_path = get_default_base_files_path() pub const supported_package_formats = ['apk', 'aab'] pub const supported_lib_folders = ['armeabi', 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'] @@ -52,6 +52,10 @@ pub: keystore Keystore base_files string = default_base_files_path overrides_path string // Path to user provided files that will override `base_files`. `java` (and later `kotlin` TODO) subdirs are recognized + prepare_base_fn fn (PackageOptions) !PackageBase = prepare_package_base // function used to prepare package base files + // Defaults (allows for runtime redefinition) + default_package_id string // Reserved for defining the default package_id at runtime + default_activity_name string // Reserved for defining the default activity_name at runtime } // verbose prints `msg` to STDOUT if `PackageOptions.verbosity` level is >= `verbosity_level`. @@ -62,6 +66,10 @@ pub fn (po &PackageOptions) verbose(verbosity_level int, msg string) { } fn get_default_base_files_path() string { + user_default_base_files_path := $d('vab:default_base_files_path', '') + if user_default_base_files_path != '' { + return user_default_base_files_path + } // Look next to the executable mut path := os.join_path(os.dir(os.real_path(os.executable())), 'platforms', 'android') if os.is_dir(path) { @@ -87,9 +95,8 @@ pub fn package(opt PackageOptions) ! { Please consult the Android documentation for details: https://developer.android.com/studio/build/application-id') } - if opt.is_prod && opt.package_id == default_package_id { - return error('${error_tag}: Package id "${opt.package_id}" is used by the V team. -Please do not deploy to app stores using package id "${default_package_id}".') + if opt.is_prod { + ensure_no_developer_team_package_id(opt)! } // Build APK match opt.format { @@ -159,7 +166,9 @@ fn package_apk(opt PackageOptions) ! { // Prepare and modify package skeleton shipped with vab // Copy assets etc. - package_path, assets_path := prepare_base(opt) + package_base := opt.prepare_base_fn(opt)! + package_path := package_base.package_path + assets_path := package_base.assets_path output_fn := os.file_name(opt.output_file).replace(os.file_ext(opt.output_file), '') tmp_product := os.join_path(opt.work_dir, '${output_fn}.apk') @@ -507,7 +516,11 @@ fn package_aab(opt PackageOptions) ! { bundletool := env.bundletool() // Run with "java -jar ..." aapt2 := env.aapt2() - package_path, assets_path := prepare_base(opt) + // Prepare and modify package skeleton shipped with vab + // Copy assets etc. + package_base := opt.prepare_base_fn(opt)! + package_path := package_base.package_path + assets_path := package_base.assets_path output_fn := os.file_name(opt.output_file).replace(os.file_ext(opt.output_file), '') tmp_product := os.join_path(opt.work_dir, '${output_fn}.aab') @@ -895,7 +908,36 @@ fn package_aab(opt PackageOptions) ! { } } -fn prepare_base(opt PackageOptions) (string, string) { +// ensure_no_developer_team_package_id returns an error if the `opt.package_id` is the +// same as `opt.default_package_id` or the same package_id domain as `android.default_package_id`. +pub fn ensure_no_developer_team_package_id(opt PackageOptions) ! { + is_default_pkg_id := opt.package_id == opt.default_package_id + if is_default_pkg_id || opt.package_id.starts_with(default_package_id) { + if opt.package_id.starts_with(default_package_id) { + return error('Do not deploy to app stores using the default package id namespace ("${default_package_id}.*")\nYou can set your own package ID with the --package-id flag') + } else { + return error('Do not deploy to app stores using the default package id "${opt.default_package_id}"\nYou can set your own package ID with the --package-id flag') + } + } +} + +pub struct PackageBase { +pub: + package_path string // Path to the base setup + assets_path string // Path to assets +} + +// prepare_package_base prepares and modifies a package skeleton and returns the paths to them. +// A "package skeleton" is a special structure of directories and files that `vab`'s +// packaging step use to make the final APK or AAB package. +// prepare_package_base is run before Java tooling does the actual packaging. +// +// Preparing includes operations such as: +// * Creating the directory structures that are returned +// * Modifying template files, like `AndroidManifest.xml` or the Java Activity +// * Moving files into place +// * Copy assets to a location where `vab` can pick them up +fn prepare_package_base(opt PackageOptions) !PackageBase { format := match opt.format { .apk { 'apk' @@ -956,23 +998,15 @@ fn prepare_base(opt PackageOptions) (string, string) { opt.verbose(1, 'Modifying base files...') - is_default_pkg_id := opt.package_id == default_package_id - if opt.is_prod && (is_default_pkg_id || opt.package_id.starts_with(default_package_id)) { - if opt.package_id.starts_with(default_package_id) { - panic('Do not deploy to app stores using the default V package id namespace "${default_package_id}"\nYou can set your own package ID with the --package-id flag') - } else { - panic('Do not deploy to app stores using the default V package id "${default_package_id}"\nYou can set your own package ID with the --package-id flag') - } - } pkg_id_split := opt.package_id.split('.') package_id_path := pkg_id_split.join(os.path_separator) os.mkdir_all(os.join_path(package_path, 'src', package_id_path)) or { panic(err) } - default_pkg_id_split := default_package_id.split('.') + default_pkg_id_split := opt.default_package_id.split('.') default_pkg_id_path := default_pkg_id_split.join(os.path_separator) native_activity_path := os.join_path(package_path, 'src', default_pkg_id_path) - activity_file_name := default_activity_name + '.java' + activity_file_name := opt.default_activity_name + '.java' native_activity_file := os.join_path(native_activity_path, activity_file_name) $if debug { eprintln('Native activity file: "${native_activity_file}"') @@ -985,7 +1019,7 @@ fn prepare_base(opt PackageOptions) (string, string) { if !is_override { // Change package id in template // r'.*package\s+(io.v.android).*' - mut re := regex.regex_opt(r'.*package\s+(' + default_package_id + r');') or { + mut re := regex.regex_opt(r'.*package\s+(' + opt.default_package_id + r');') or { panic(err) } mut start, _ := re.match_string(java_src) @@ -1018,7 +1052,7 @@ fn prepare_base(opt PackageOptions) (string, string) { java_src) or { panic(err) } // Remove left-overs from vab's copied skeleton - if opt.package_id != default_package_id { + if opt.package_id != opt.default_package_id { os.rm(native_activity_file) or { panic(err) } mut v_default_package_id := default_pkg_id_split.clone() for i := v_default_package_id.len - 1; i >= 0; i-- { @@ -1173,6 +1207,7 @@ fn prepare_base(opt PackageOptions) (string, string) { opt.verbose(1, 'Copying assets...') + is_default_pkg_id := opt.package_id == opt.default_package_id if !is_default_pkg_id && os.is_file(opt.icon) && os.file_ext(opt.icon) == '.png' { icon_path := os.join_path(package_path, 'res', 'mipmap') paths.ensure(icon_path) or { panic(err) } @@ -1250,7 +1285,10 @@ fn prepare_base(opt PackageOptions) (string, string) { } } } - return package_path, assets_path + return PackageBase{ + package_path: package_path + assets_path: assets_path + } } pub fn is_valid_package_id(id string) ! { diff --git a/cli/options.v b/cli/options.v index 82b6769..cd73307 100644 --- a/cli/options.v +++ b/cli/options.v @@ -41,18 +41,23 @@ pub: screenshot_delay f64 @[xdoc: 'Wait for this amount of seconds before taking screenshot'] screenshot_on_log string @[xdoc: 'Wait for this string to appear in the device log before taking a screenshot'] screenshot_on_log_timeout f64 = -1.0 @[xdoc: 'Timeout after this amount of seconds if --screenshot-on-log string is not detected'] + // Defaults (allows for runtime redefinition) + default_package_id string = android.default_package_id @[ignore] // Reserved for defining the default package_id at runtime + default_activity_name string = android.default_activity_name @[ignore] // Reserved for defining the default activity_name at runtime pub mut: // I/O input string @[tail] // NOTE: vab also supports passing input as *first* argument output string @[short: o; xdoc: 'Path to output (dir/file)'] additional_args []string @[ignore] // additional_args collects arguments (*not* flags) that could not be parsed // App essentials - app_name string = android.default_app_name @[long: name; xdoc: 'Pretty app name'] - icon string @[xdoc: 'App icon'] - package_id string = android.default_package_id @[xdoc: 'App package ID (e.g. "org.company.app")'] - activity_name string @[xdoc: 'The name of the main activity (e.g. "VActivity")'] - package_format string = android.default_package_format @[long: package; xdoc: 'App package format (.apk/.aab)'] - package_overrides_path string @[long: 'package-overrides'; xdoc: 'Package file overrides path (e.g. "/tmp/java")'] + icon string @[xdoc: 'App icon'] + app_name string = android.default_app_name @[long: name; xdoc: 'Pretty app name'] + package_format string = android.default_package_format @[long: package; xdoc: 'App package format (.apk/.aab)'] + // "VIP" fields. The contents of these needs to match several things thoughout + // the project being built. This includes matches in Java sources - the fields are also needed for launching. + // See also: default_package_id, default_activity_name above and `ensure_launch_fields/0`. + package_id string = android.default_package_id @[xdoc: 'App package ID (e.g. "org.company.app")'] + activity_name string @[xdoc: 'The name of the main activity (e.g. "MyActivity")'] // Build and packaging archs []string = android.default_archs @[ignore] // Compile for these archs. (parsed specially to support "arch,arch,arch") is_prod bool @[ignore] // Parsed and inferred from V specific flags @@ -66,6 +71,7 @@ pub mut: keystore_alias string @[xdoc: 'Use this keystore alias from the keystore file to sign the package'] keystore_password string @[ignore] // Resolved at runtime via env var see: Options.resolve() keystore_alias_password string @[ignore] // Resolved at runtime via env var see: Options.resolve() + package_overrides_path string @[long: 'package-overrides'; xdoc: 'Package file overrides path (e.g. "/tmp/java")'] // Build specifics build_tools string @[xdoc: 'Version of build-tools to use (--list-build-tools)'] api_level string @[long: 'api'; xdoc: 'Android API level to use (--list-apis)'] @@ -442,7 +448,7 @@ pub fn (mut opt Options) extend_from_dot_vab() { opt.app_name = vab_app_name } } - if opt.package_id == android.default_package_id && dot_vab.contains('package_id:') { + if opt.package_id == opt.default_package_id && dot_vab.contains('package_id:') { vab_package_id := dot_vab.all_after('package_id:').all_before('\n').replace("'", '').replace('"', '').trim(' ') if vab_package_id != '' { @@ -516,10 +522,10 @@ pub fn (mut opt Options) extend_from_dot_vab() { pub fn (mut opt Options) ensure_launch_fields() { // If no package id or activity name has set, use the defaults if opt.package_id == '' { - opt.package_id = android.default_package_id + opt.package_id = opt.default_package_id } if opt.activity_name == '' { - opt.activity_name = android.default_activity_name + opt.activity_name = opt.default_activity_name } } @@ -909,6 +915,9 @@ pub fn (opt &Options) as_android_package_options() android.PackageOptions { libs_extra: opt.libs_extra output_file: opt.output overrides_path: opt.package_overrides_path + // Transfer defaults + default_package_id: opt.default_package_id + default_activity_name: opt.default_activity_name } return pck_opt } diff --git a/docs/docs.md b/docs/docs.md index ce50b2a..198503f 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -10,6 +10,7 @@ Welcome - and have a productive time using V and `vab`! - [Introduction](#introduction) - [Developing `vab`](#developing-vab) - [The V to Android App Development Cycle](#the-v-to-android-app-development-cycle) +- [Tweaking defaults at compile time](#tweaking-defaults-at-compile-time) - [Using `vab` from the command line](#using-vab-from-the-command-line) - [Using `vab` programmatically](#using-vab-programmatically) - [Examples](#examples) @@ -90,6 +91,20 @@ needed for the Android app to be able to start on the device OS. The developer is expected to take care of bullet point `1.` while `vab` will help cover the rest. +# Tweaking defaults at compile time + +`vab` tries to strike a balance and be sane and fair about what default values are +used. Should the defaults not fit your daily use they can be tweaked via V's +powerful compile-time defines (`-d ident=value`). + +The following is a list of values that can be tweaked at compile time: +* `default_app_name` via `-d vab:default_app_name='V Test App'` +* `default_package_id` via `-d vab:default_package_id='io.v.android'` +* `default_activity_name` via `-d vab:default_activity_name='VActivity'` +* `default_package_format` via `-d vab:default_package_format='apk'` +* `default_min_sdk_version` = `-d vab:default_min_sdk_version=21` +* `default_base_files_path` via `-d vab:default_base_files_path=''` + # Using `vab` from the command line For now please use the [Usage](https://github.com/vlang/vab#usage) section of the README.md diff --git a/vab.v b/vab.v index 39cfa79..1c57f71 100644 --- a/vab.v +++ b/vab.v @@ -192,7 +192,7 @@ fn main() { // Validate environment after options and input has been resolved opt.validate_env() - + // Ensure the "VIP" fields are sat before launch opt.ensure_launch_fields() // Keystore file