Megaton Documentation

Table of Contents

Having an issue?

Please provide feedback, bug reports and feature requests on GitHub.

Installation

Prerequisites

  • Megaton requires DevKitA64 from DevKitPro. (Select the switch-dev group)

    Todo

    TODO Add info about how megaton searches for DKP installation

  • For Rust support (building mods written in Rust), a Rust toolchain for the host system (i.e. the system used to build the mod) and git are required to clone and build the Rust compiler for the aarch64-unknown-hermit target.

Install Megaton

The megaton CLI is a single binary, which can be installed in 3 ways:

Todo

TODO These don’t work yet

  1. (Recommended) Install from prebuilt binary with cargo-binstall
    cargo binstall megaton-cmd --git https://github.com/Pistonite/megaton
    
  2. Install from source with cargo
    cargo install megaton-cmd --git https://github.com/Pistonite/megaton
    
  3. Download the binary from the latest release on GitHub

Install Megaton Rust Toolchain

Megaton implements the Hermit ABI to bind Rust Standard Library to NNSDK. This requires a custom Rust toolchain. Megaton will install this toolchain at ~/.cache/megaton/rust-toolchain.

This toolchain is locked at a commit for every Megaton version. This will be bumped approximately in line with Rust’s stable release. If you need Rust feature in a higher version of Rust than what is included with Megaton, please open an issue on GitHub.

Run the following command to install the toolchain, or upgrade the toolchain in the future

megaton toolchain install

Note

This compiles LLVM and bootstraps the Rust compiler, which will take a while. To keep the build artifact, use the -k/--keep flag: megaton toolchain install -k. This will make it faster to rebuild the toolchain in the future when upgrading. However, this will consume 10-20GB of disk spaces.

You can check if the toolchain is installed with

megaton toolchain check

Or directly with rustc (megaton is the name of the toolchain)

rustc +megaton -vV

Upgrading

To upgrade megaton, simply reinstall the latest version with cargo/cargo-binstall, or replace the binary with the latest release on GitHub if you installed it manually.

To upgrade the custom Rust toolchain, run megaton toolchain install after upgrading megaton.

TUTORIAL - TODO

Profiles

The profile system allows you to have different configurations/targets for your project. An example is having different build flags and dependencies when targeting different versions of a game.

The [build] and [check] sections supports profiles using the profiles key in the section. For example, [build.profiles.foo] has the same schema as [build], and should contain configuration for the foo profile.

[build]
sources = ["src"]

[build.profiles.foo]
sources = ["src_foo"]

Profiles can be selected when building by using megaton build --profile PROFILE. For the example above, running megaton build -p foo will include both src and src_foo as source directories, while running megaton build will only include src as the source directory.

Nested map properties like build.flags are recursively merged, and should be specified like [build.profiles.foo.flags], NOT [build.flags.profiles.foo]

Base Profile

Danger

The base profile will be called "none", as described in this documentation. However in the current development, it’s coded as "base". This will be changed in the stable release, and is tracked by Issue #80

The configs without any explicit profiles is known as the “base profile”, and has the name "none". (This word is reserved you cannot name your custom profile "none").

Inheriting the Base Profile

Each config option specified on a custom profile inherits from the base profile. The inheritance uses one of two inheritance behaviors.

  • Override: The value for this key will override that of its parent.
    • This is always the case for scalar (non-array) values
    • If the value is array type, it can still optionally extend the base by including "<default>" in the array.
  • Append: Only applies to arrays. The value for this key is the appended to that of its parent. This means that a profile will always extend the default behavior but cannot disable it.

See the reference for Build and Check sections for the behavior of each config option.

Configure Defaults

You can customize the profile selection behavior of the CLI with the [profile] section of the config, for example, select a profile by default if nothing is specified on the CLI, or disallow the "none" profile (i.e. the base profile) from the CLI (useful if only using the base profile) for inheritance.

See the reference for Profile section for more information.

References

This section covers the behavior of Megaton in detail.

Megaton TOML Config Spec

This config allows the user to set several options which change how the mod is built by the build tool.

The root of a particular project is the directory that contains the config (Megaton.toml). For all values that determine a path, unless otherwise specified, the path is relative to the project root.

For each key, if a default value/behavior is not specified, the entry is required in the config.

The expected type for each key is listed in its heading. Additional restrictions may be specified in the description if a specific form is expected.

Profile enabled keys/sections

Profile enabled keys are options which can have a unique value for different profiles. To set a value for a specific profile, use the key format {section}.profiles.{profile-name}.{key-name}. If a profile enabled key is set without specifying a profile, i.e. {section}.{key-name}, the value will be set for the base profile. If a section is marked as profile enabled, all keys under that section are profile enabled.

Profile inheritance behavior

Each config options set for a profile uses one of two inheritance behaviors. The parent of a user-specified profile is the base profile. The “parent” of the base profile is the base profile’s default value. If a profile enabled key is not an array type, it will always override its parent.

  • Append: The value for this key is the appended to that of its parent. This means that a profile will always extend the default behavior but cannot disable it.
  • Override: The value for this key will override that of its parent. These values can still optionally extend their parent by including “” in their value. If the values is specified as [], the parent value will be completely disabled.

Configuration for the Megaton build tool and library.

Example:

[megaton]
version = "1"

Tip

For each key, if a default value/behavior is not specified, it is required in the config. Otherwise it is optional.

Key: megaton.version

Type: string

Version of Megaton the project is supposed to use. Megaton will abort if its minor version does not match this value. (i.e. "2" will match 0.2.x). For future-proof, this value is a string rather than an integer.

Key: megaton.custom-entry

Type: string

The entry point (main function) symbol passed to the linker.

If non-empty, Megaton library will NOT be compiled or linked when running megaton build. Use this option to use Megaton as a standalone build tool, without the Megaton library. When using Megaton library, the megaton_main function is not the real “main function”; instead, it is wrapped with a function that initializes Megaton and calls megaton_main.

Warning

Megaton library is required to use Rust. If megaton.custom-entry is true, Rust is also disabled.

Default: "" (use Megaton library)

In a Megaton project, the module is the NSO object that is ultimately produced and loaded alongside the game. It is also sometimes synonymus with project.

Example:

[module]
name = "my-mod"
title-id = 0x01007ef00011e000

Tip

The root of a particular project is the directory that contains the config (Megaton.toml). For all values that determine a path, unless otherwise specified, the path is relative to the project root.

For each key, if a default value/behavior is not specified, it is required in the config. Otherwise it is optional.

Key: module.name

Type: string

The name of the module, i.e., your mod’s name. The final binary will use this name.

Restrictions: Cannot be "" or "lib". Only alphanumeric characters, -, and _ are allowed.

Key: module.title-id

Type: integer

The title ID for the targeted game. Needed to generate the NPDM file. (Note this needs to be an integer in the config, not a hex string)

Key: module.target

Type: string

The path to the directory for the Megaton build tool to emit build artifacts. All the library and generated artifacts will be placed here under a subdirectory megaton. (For example if module.target = "foo/bar", then all megaton’s output will be at foo/bar/megaton/.) See Output Directory for the structure of the directory.

Default: "target"

Key: module.compdb

Type: string

A database of compiler commands to be used for clangd integration. Megaton will work with existing compile_commands.json (included ones generated from another Megaton project). This allows multiple Megaton projects in the same monorepo to share the same compile_commands.json to make LSP integration easier. However, other tools like CMake could still override megaton’s entries

Default: "compile_commands.json"

Megaton allows multiple different config profiles to coexist. You can set up profiles for different build configurations like release and debug, or different versions of a game. See the tutorial for Profile for more info.

Tip

For each key, if a default value/behavior is not specified, it is required in the config. Otherwise it is optional.

Key: profile.allow-base

Type: bool

Determines if the base profile profile is allowed to be built.

Default: true

Key: profile.default

Type: string

The profile that will be built if the -p PROFILE flag is omitted from the CLI. If set to "" (the empty string), the profile must be set via CLI flag on every call.

Default: "none"

Examples:

  1. Set to empty string
    [module]
    name = "example"
    
    [profile]
    default = "" # Must run with `megaton build -p PROFILE`
    
  2. Set to non-empty string
    [module]
    name = "example"
    
    [profile]
    default = "foo" 
    # Run with `megaton build` -> use foo
    # Run with `megaton build -p none` -> use none (base profile)
    
  3. Set with allow-base = false
    [module]
    name = "example"
    default = "foo" 
    allow-base = false
    # Run with `megaton build` -> use foo
    # Run with `megaton build -p none` -> error
    

The [build] section is where to change compilation settings.

Example:

[build]
sources = ["src"]
includes = ["include", "some_lib/include"]

[build.flags]
c = ["<default>", "-DMY_DEFINE=1"]
rust = ["<default>", "-Zub-checks=yes"]
cargo = ["<default>", "--features", "my-feature"]

Tip

The root of a particular project is the directory that contains the config (Megaton.toml). For all values that determine a path, unless otherwise specified, the path is relative to the project root.

For each key, if a default value/behavior is not specified, it is required in the config. Otherwise it is optional.

This section can be extended with profiles.

Key: build.sources

Type: string[] (array of strings)

Paths to source directories to recursively scan for C/C++/Assembly source files. Sources generated by Megaton are automatically included and do not need to be specified. If the mod contains only Rust code, this can be omitted. Sources will be detected and matched based on file extension. The following extensions will be detected:

  • C: .c
  • C++: .cc, .c++, .cpp
  • Assembly: .s, .asm

Inheritance: Append

Default: []

Key: build.includes

Type: string[] (array of strings)

Paths to include directories to be passed to the compiler as -I flags. Headers generated by Megaton (including headers from the Megaton library) are automatically included and do not need to be specified.

Inheritance: Append

Default: []

Key: build.libpaths

Type: string[] (array of strings)

Paths to library directories to be passed to the compiler as -L flags.

Inheritance: Append

Default: []

Key: build.libraries

Type: string[] (array of strings)

Libraries to be linked. These will be passed as -l flags to the linker. The names of libraries here must be discoverable in the directories specified in build.libpaths. For example, to link the library “foo”, add “foo” to this array and place the file libfoo.so in one of the library paths.

Inheritance: Append

Default: []

Key: build.objects

Type: string[] (array of strings)

Additional .o and .a objects to link with, i.e. compiled objects that are not generated by Megaton.

Inheritance: Append

Default: []

Key: build.flags

Build flags to pass to the different tools on the build toolchain. All flags keys have the same inheritance behavior of Override. In order to add a build flag, specify the value like this: [<"default">, -DDEBUG]. If the value is specified as [], The default flags will be disabled for that profile.

The default flags can be found here, and the flag extension behavior is detailed below.

Property<default> includes
commonNone
ccommon
cxxc
ascxx
ldcommon
rustNone
cargoNone

Example

For this flag config:

[build.flags]
c = ["<default>", "-DDEBUG"]

-DDEBUG will be added to the flags when invoking gcc (using C flags), g++ (using CXX flags, extended from C flags), and as (using AS flags, extended from CXX flags).

Todo

Replace the link above with link to the GitHub source code where flags are specified

Inheritance: Override

Default: ["<default>"]

Key: build.flags.common

Flags for all tools except rust and cargo.

Key: build.flags.c

Flags for the C compiler.

Key: build.flags.cxx

Flags for the C++ compiler.

Key: build.flags.as

Flags for the assembler to use with assembly sources.

Key: build.flags.ld

Flags for the linker.

Warning

C++ compiler is used for linking. Flags for ld should be specified as -Wl,--flag instead of --flag.

Key: build.flags.rust

Flags for rust. Corresponds to the RUSTFLAGS environment variable. Rust flags can also be specified in Cargo.toml, but placing them here allows them to be dynamically enabled using Megaton’s profile system.

Restrictions: Can only be specified if cargo.enabled = true

Key: build.flags.cargo

Flags to be passed to cargo, such as feature flags.

Restrictions: Can only be specified if cargo.enabled = true

Megaton can be used to build mods that contain both C++ and Rust code. Internally Megaton uses Cargo to compile Rust sources. It also provides support for the cxx crate to handle interop between C++ and Rust (if needed). Rust crates should be configured, as usual, in the Cargo.toml file. This sections controls Megaton options for Cargo and CXX.

Tip

The root of a particular project is the directory that contains the config (Megaton.toml). For all values that determine a path, unless otherwise specified, the path is relative to the project root.

For each key, if a default value/behavior is not specified, it is required in the config. Otherwise it is optional.

Key: cargo.enabled

Type: bool

Determines if Rust support is enabled. Megaton will only try to do Rust stuff like cargo build if this is true.

Restrictions: May not be true if megaton.custom-entry is not "". See [megaton] config section.

Default: Megaton will try to determine if Rust support should be enabled using the following checks, in order:

  • If cargo.manifest is specified -> true
    • It will error if the specified path cannot be found.
  • If the file Cargo.toml exists in project root -> true
  • Else -> false

Key: cargo.manifest

Type: string

The manifest file path for cargo. This will be passed to --manifest-path when cargo is run.

Default: "Cargo.toml" if it exists, cargo.enabled will be false if it does not exist.

Key: cargo.sources

Type: string[] (array of strings)

The Rust source directories to scan for .rs files. These files will be scanned for cxx::bridge attributes to generate FFI code.

Default: ["src"]

Key: cargo.header-suffix

Type: string

Suffix to generated headers. The user can put the empty string here so they can do #include <foo/lib.rs> instead of <foo/lib.rs.h>

Default: ".h"

Megaton performs a check step after linking the module into an ELF and before converting it to a .nso file. This check allows Megaton to blacklist certain symbols and instructions, preventing an unstable binary from being created to catch bugs earlier.

Tip

The root of a particular project is the directory that contains the config (Megaton.toml). For all values that determine a path, unless otherwise specified, the path is relative to the project root.

For each key, if a default value/behavior is not specified, it is required in the config. Otherwise it is optional.

This section can be extended with profiles.

Key: check.ignore (array of strings)

A list of symbols that the checker will ignore when checking the built binary.

Inheritance: Override

Default: ["<default>"]

Todo

Link to the code where list of default symbols that are ignored (or the default behavior)

Key: check.symbols (array of strings)

Type: string[] (array of strings)

Paths to symbol files generated by objdump -T.

Inheritance: Append

Default: []

Key: check.disallowed-instructions

Type: string[] (array of strings)

Instructions that are disallowed in the final binary. Place instructions that are known to crash here. (Mostly needed for Megaton library development).

Inheritance: Override

Default: ["<default>"]

Todo

Link to the code where list of default symbols that are ignored (or the default behavior)