Getting Started
We recommend you developing the kernel in Debian-12.0+ or Ubuntu-24.04+ environment to get the best tooling support.
Prepare basic development environment
repo
We are using repo to manage the kernel project.
Please follow https://mirrors.tuna.tsinghua.edu.cn/help/git-repo/ to install repo.
gn
We are using GN to organize and build the BlueOS project, rather than
cargo the official package manager of Rust eco system. gn offers better
multi-language support and better build speed than cargo.
You can download prebuilt gn binaries from https://gn.googlesource.com/gn/#getting-a-binary. Put the downloaded binary
to a directory and ensure this directory is in your ${PATH}.
Install packages on Linux
Install packages shipped by the distro.
sudo apt install build-essential cmake ninja-build pkg-config \
libssl-dev gdb-multiarch curl git wget \
libslirp-dev python3 python3-pip meson \
libglib2.0-dev flex bison libfdt-dev \
gcc-riscv64-unknown-elf clang llvm lld \
python3-kconfiglib python3-tomli
Additionally, download and install arm toolchains
wget https://developer.arm.com/-/media/Files/downloads/gnu/14.3.rel1/binrel/arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi.tar.xz
tar xvf arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi.tar.xz -C <install-path>
wget https://developer.arm.com/-/media/Files/downloads/gnu/14.3.rel1/binrel/arm-gnu-toolchain-14.3.rel1-x86_64-aarch64-none-elf.tar.xz
tar xvf arm-gnu-toolchain-14.3.rel1-x86_64-aarch64-none-elf.tar.xz -C <install-path>
Add <install-path>/arm-gnu-toolchain-14.3.rel1-x86_64-aarch64-none-elf/bin and <install-path>/arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi/bin to your $PATH.
Build and install QEMU on Linux
Download QEMU source code tarball,
wget https://download.qemu.org/qemu-10.0.2.tar.xz
tar xvf qemu-10.0.2.tar.xz
cd qemu-10.0.2
mkdir build && cd build
../configure --prefix=<install-path> --enable-slirp && \
make -j$(nproc) install
Add <install-path>/bin to your $PATH.
Install packages on macOS
brew install coreutils llvm@19 lld@19 gcc-arm-embedded cmake ninja qemu
brew tap riscv-software-src/riscv
brew install riscv-tools riscv64-elf-gcc riscv64-elf-binutils riscv64-elf-gdb
python3 -m pip install --user --break-system-packages --upgrade kconfiglib
For aarch64 toolchain, please refer to arm-gnu-toolchain. It’s recommended to download tarballs rather than pkgs. For RISC-V toolchain on macOS, please refer to homebrew-riscv.
Code formatters
We are using code formatters of corresponding programming language to keep our code style consistent. These formatters can be installed via
# On Linux
sudo apt install clang-format yapf3
# On macOS
brew install clang-format yapf
Here’s the table of format command and its corresponding programming language.
| lang | format command |
|---|---|
| Rust | rustfmt |
| C/C++ | clang-format |
| Python | yapf3 |
| GN | gn format |
Init and sync the project
Use following command to init the project.
mkdir blueos-dev
cd blueos-dev
If you have configured public ssh key on github, please use following commands
repo init -u git@github.com:vivoblueos/manifests.git -b main -m manifest.xml
otherwise, please try
repo init -u https://github.com/vivoblueos/manifests.git -b main -m manifest.xml
Then sync all repositories in the project.
repo sync
You might accelerate the synchronization by appending -j$(nproc) to the above command.
Building the Rust toolchain for BlueOS kernel
We have forked upstream Rust compiler to support BlueOS kernel
targeted to *-vivo-blueos-* and BlueOS kernel’s Rust-std.
We’ll finally contribute our changes to the upstream repository and make *-vivo-blueos-*
a tier Rust target.
Clone the downstream repository
Run If you have configured public ssh key on github, please use following commands,
git clone git@github.com:vivoblueos/rust.git
git clone git@github.com:vivoblueos/cc-rs.git
git clone git@github.com:vivoblueos/libc.git
otherwise, please try
git clone https://github.com/vivoblueos/rust.git
git clone https://github.com/vivoblueos/cc-rs.git
git clone https://github.com/vivoblueos/libc.git
The blueos-dev branch is set as default, so no manual branch switching is required.
Setup Rust mirror site
In China, we recommend you to use mirror site for crates.io and rustup. Add following lines to your ~/.bashrc
export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static
export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
and then type
source ~/.bashrc
Install via x script
Run the following commands in your bash shell. These instructions work for both Linux and macOS platforms:
export CARGO_NET_GIT_FETCH_WITH_CLI=true
export DESTDIR=<choose-your-install-prefix>
cd rust
cp config.blueos.toml config.toml
./x.py install -i --stage 1 compiler/rustc
./x.py install -i --stage 1 library/std --target aarch64-vivo-blueos-newlib
./x.py install -i --stage 1 library/std --target thumbv7m-vivo-blueos-newlibeabi
./x.py install -i --stage 1 library/std --target thumbv8m.main-vivo-blueos-newlibeabihf
./x.py install -i --stage 1 library/std --target riscv64-vivo-blueos
./x.py install -i --stage 1 library/std --target riscv32-vivo-blueos
./x.py install -i --stage 1 library/std --target riscv32imc-vivo-blueos
./x.py install -i --stage 0 rustfmt
./x.py install -i --stage 0 rust-analyzer
./x.py install -i --stage 0 clippy
You must also install the host machine’s standard library and LLVM tools.
For Linux:
./x.py install -i --stage 1 library/std --target x86_64-unknown-linux-gnu
cp -rav build/x86_64-unknown-linux-gnu/llvm/{bin,lib} ${DESTDIR}/usr/local
For macOS:
./x.py install -i --stage 1 library/std --target aarch64-apple-darwin
cp -av build/aarch64-apple-darwin/llvm/{bin,lib} ${DESTDIR}/usr/local
To use the kernel toolchain, add the following to your environment:
export PATH=${DESTDIR}/usr/local/bin:${PATH}
Or if you want to manage the blueos toolchain using rustup, you can try:
ln -s ${DESTDIR}/usr/local ~/.rustup/toolchains/blueos-dev
rustup default blueos-dev
Build kernel image
The kernel currently supports multiple boards.
- qemu_mps2_an385
- qemu_mps3_an547
- qemu_virt64_aarch64
- qemu_riscv64
To build a kernel image for specified board, like qemu_mps2_an385, use command
gn gen out/qemu_mps2_an385.release/ --args='build_type="release" board="qemu_mps2_an385"'
ninja -C out/qemu_mps2_an385.release
To run tests, type
ninja -C out/qemu_mps2_an385.release check_all
Args and their semantics.
| arg | semantics |
|---|---|
| build_type | Configuration for the build |
| board | Name of the board targeted to |
Use rust-analyzer in VSCode
We recommend you to use rust-analyzer extension in VSCode to support development of BlueOS kernel.
However, since we are using gn, rather than Cargo.toml, to manage our project, the rust-analyzer extension in VSCode might not work
out-of-box. You should use following commands1 to get the extension work
gn gen out/qemu_mps2_an385 --export-rust-project
ln -sfn out/qemu_mps2_an385/rust-project.json
-
https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/rust.md#using-vscode ↩
QEMU checker
Most kernel code are tested via QEMU. We introduce QEMU checker to assist testing the kernel.
QEMU checker must be provided with a QEMU runner script and a input file containing check directives. QEMU checker runs the runner script and captures output lines of it. If output lines match conditions or regex specified in the check directives, the checker takes corresponding actions. Directives should be written at the header of the input file.
| directive | action |
|---|---|
| // CHECK-FAIL: <regex> | Record this line and report at exit. |
| // CHECK-SUCC: <regex> | Record this line and report at exit. |
| // ASSERT-FAIL: <regex> | Exit the runner and report failure. |
| // ASSERT-SUCC: <regex> | Exit the runner and report success. |
| // NEWLINE-TIMEOUT: <number> | Timeout when checker reads the next line. Report failure if timeout occurs. |
| // TOTAL-TIMEOUT: <number> | Total timeout for this run. |
Example
Suppose we have a kernel image for testing, named blueos_foo_test.
First we have to generate a qemu runner script for it.
gen_qemu_runner("runner_for_blueos_foo_test") {
img = ":blueos_foo_test"
qemu = "$qemu_exe"
board = "$board"
}
Then, we make a checker for the above runner.
run_qemu_checker("check_blueos_foo_test") {
img = ":blueos_foo_test"
runner = ":runner_for_blueos_foo_test"
checker = "//kernel/kernel/tests/integration_test.rs"
}
Must be noted, the above two targets must be put in the same BUILD.gn.
Check directives should be put at the header of integration_test.rs.
// NEWLINE-TIMEOUT: 15
// ASSERT-SUCC: Kernel test end.
// ASSERT-FAIL: Backtrace in Panic.*
Integrate checks in CI
CI runner only runs two toplevel targets, default and check_all.
To run check during CI, you have to put your checker target in deps of
check_all group in the toplevel BUILD.gn.
group("check_all") {
deps = [ ":check_kernel" ]
}
Run Coverage
We generate the code needed for coverage statistics by adding -Cinstrument-coverage during compilation, and generate coverage data through the integration of minicov and semihosting. Finally, we use the grcov tool to generate readable coverage data in HTML format. All of these have been integrated into our build system, and coverage data can be generated using the following commands:
gn gen out/qemu_riscv64.cov/ --args='build_type="coverage" board="qemu_riscv64"'
ninja -C out/qemu_riscv64.cov check_coverage
If you get a prompt that grcov is not found, you can install it via
cargo install grcov
After building and running, you can find merged coverage report in the
./out/qemu_riscv64.cov/cov_report directory. Open the index.html
file in the directory to view the coverage data.
Run shell in QEMU
We have implemented a simple shell to demonstrate the functionality of the BlueOS kernel. First let’s build the shell executable.
gn gen out/shell_test --args='board="qemu_mps2_an385" build_type="release"'
ninja -C out/shell_test shell_runner
The ninja will output a line at last, which is like
Generated /build/vivoblueos/out/shell_test/bin/shell_runner-qemu.sh
Now we can run the shell via the generated script. If everything is going smooth, we can see
Hello, shell!
>
Please type help for playing with this shell.
Add third party crates
We manage all third party crates in the https://github.com/vivoblueos/external repository.
All crates are transformed into GN’s BUILD.gn files via gnrt which is initially created by the Chromium project.
Add dependency in //external/Cargo.toml
To add a new third party crate, for example unwinding, add following code in //external/Cargo.toml
[dependencies.unwinding]
version = "=0.2.4"
default-features = false
features = [
"unwinder"
]
Then run ninja -C <out> run_gnrt.
After the above two steps, developers can add //external/vendor/unwinding-0.2.4:unwinding to the deps field in the target defintions in BUILD.gn.
Add a new syscall
Adding a new syscall in the kernel is easy.
There are 3 steps.
- Add new syscall number for the new syscall in
header/src/lib.rs.
pub enum NR {
Read,
}
- Implement the new syscall in
kernel/src/syscall_handlers/mod.rs.
This is generally done via define_syscall_handler! if the syscall is trivial. For example
define_syscall_handler!(
read(fd: i32, buf: *mut i8, len: usize) -> c_long {
// Implement your vfs read.
});
- Register the new syscall in
kernel/src/syscall_handlers/mod.rs.
Add a new entry in syscall_table!. For example,
(Read, read),
Must be noted, the first operand MUST be the unqualified name of the NR enumeration.
Invoke a syscall
BlueOS kernel offers two modes of invoking syscall.
- Software interruption
This is generally seen in most OS kernels to switch from user space to kernel space.
- Direct invoking
In this mode, syscall handlers are invoked directly via function calls.
In both modes, if you have to invoke a syscall, use the bk_syscall! macro in blueos_scal crate.
For example,
use blueos_scal::bk_syscall;
use blueos_header::syscalls::NR::{Read};
fn read_something(fd: i32, buf: *mut i8, len: usize) -> i32 {
bk_syscall!(Read, buf, len) as i32
}
There is no need to change your code when switching to another mode.
By default, SWI mode is used. If you want to use the direct invoking mode,
what you have to do is passing --cfg direct_syscall_handler to build the kernel.
Kernel’s basic data types
Arc
A customized Arc(infra/src/tinyarc.rs), is implemented for
the kernel. Compared to alloc::sync::Arc, there is no much
difference, except it has only strong_count thus reduced its size
and is friendly to embedded devices. Also its memory layout is known
to blueos_infra, so our instrusive list can cooperate with it
easily.
Intrusive list
blueos_infra’s ilist(infra/src/list/typed_ilist.rs) is typed and
unsafe. It’s like C-style’s ilist, however we recommend developers
not using it directly but with smart pointers. We implement ArcList
on top of the typed ilst with Arc mentioned above and guarantees safety.
How to use the blueos repository
Relationship with the component repositories
Previously, components such as kernel, build, libc, and book were maintained
in separate Git repositories. The manifests repository described their URLs,
revisions, and local paths. Developers used repo init and repo sync to
assemble a complete source workspace. Each component remained a separate Git
repository with its own branches, commit history, and pull requests (PRs).
Changes spanning several repositories required a branch and a PR in each repository, making review and CI validation harder to coordinate.
The blueos repository brings these components together. Its component paths
match those in the workspace created with repo init, and Josh preserves
the imported commit history.
Basic workflow
- For new work, usually create a branch from the central repository’s
main. - For work already started in a component repository, import the branch and integrate it with the complete central history. Continue development there and publish a central branch and PR.
Choose where to develop and submit PRs
Keep the development branch and PR for a task in the central repository where possible. Avoid maintaining the same branch in both repositories. If several people share the original branch, agree on how to migrate it and where further development will take place.
| Situation | Recommended workflow |
|---|---|
| New work based on default branches | Start from the complete central main. |
| Existing component branch or open PR | Import it and publish a central PR. |
| Dependencies on open branches or PRs | Integrate the work and dependencies. |
| Changes across several components | Develop, validate, and submit centrally. |
Changes limited to book or another independent component can also be submitted directly to that repository. Import and integrate any unmerged dependencies together. If a dependency is already a complete central branch, use Git to merge it or transplant its commits. After migrating a PR, close the original and explain the migration to avoid merging the work twice.
Import component branches
Install the tools
You need Python 3, Git, josh, and josh-filter. The following command
installs both Josh programs:
cargo +stable install josh-cli --locked \
--git https://github.com/josh-project/josh.git \
--tag r26.07.19
Use a standard Rust toolchain for installation rather than blueos’s custom Cargo.
Configure remotes and fetch branches
A Josh remote places the component’s files under the corresponding central
directory. For example, :prefix=build maps the component repository’s root
to build/ in the central repository.
Component name (--repo) | Josh remote filter |
|---|---|
apps_example | :prefix=apps/example |
apps_shell | :prefix=apps/shell |
book | :prefix=book |
build | :prefix=build |
external | :prefix=external |
kernel | :prefix=kernel |
libc | :prefix=libc |
librs | :prefix=librs |
Configure the remotes you need. Skip remotes that are already configured:
See the appendix for the complete template.
josh remote add fork-build \
https://github.com/{{YOUR_USERNAME}}/build.git ':prefix=build'
josh remote add fork-kernel \
https://github.com/{{YOUR_USERNAME}}/kernel.git ':prefix=kernel'
josh fetch --remote fork-build
josh fetch --remote fork-kernel
git --no-pager branch --list --all
The remote names fork-build and fork-kernel are local names you choose.
After fetching, select an imported branch using a remote-tracking reference
such as fork-build/branch-name. Configure upstream repositories the same
way, changing the remote name and URL.
josh fetch updates remote-tracking references. It does not move your local
development branch or integrate the imported branch into the full central
history.
Integrate imported branches with the complete blueos repository
An imported branch contains only its component’s files. Integrate it with the central history containing all components before using it as a complete central branch and publishing a central PR.
The early central history imported the component repositories in stages.
In vivoblueos/blueos, the commit completing these initial imports is
a0d6549f6078a624dc513d4d2a34f5cf47e02f13. Usually start from the central
main or a central branch that already integrates the previous imports.
After integration, check that the initial import commit is an ancestor:
git merge-base --is-ancestor \
a0d6549f6078a624dc513d4d2a34f5cf47e02f13 \
branch-name
An exit code of 0 confirms the ancestry. This checks the history relationship; you still need to validate the resulting files and build for your changes.
You can first test a merge or rebase in the component repository. The central repository may contain changes that have not yet been synchronized back, so central integration can still require conflict resolution and validation.
Integrate with rebase
Use Git rebase to transplant the imported branch’s changes onto the central
main. The examples assume that the component main changes excluded by
rebase are already present in the central starting point.
# Transplant build's miri/allocator branch onto the full central history.
git switch -c import/build-miri-rebased fork-build/miri/allocator
git rebase --onto main fork-build/main
# The result can now be used as a complete central branch.
# Add kernel's changes on top of the previous result.
git switch -c blueos/miri/allocator-rebased fork-kernel/miri/allocator
git rebase --onto import/build-miri-rebased fork-kernel/main
git push origin blueos/miri/allocator-rebased
Integrate with merge
Merging a raw Josh import into the central main also brings in its full
history. Changes already synchronized to the central repository can have
different SHAs under different transformations, so they may reappear as extra
commits in the branch and PR.
Use merge-subtree-branches.py to handle this mapping. Each invocation
integrates one imported branch with a merge. Run it sequentially to import
multiple components:
# Import build's miri/allocator branch starting from the central main.
python3 tools/josh/merge-subtree-branches.py \
--repo build \
--source-remote fork-build \
--source-branch miri/allocator \
--target-base main \
--target-branch blueos/miri/allocator
python3 tools/josh/merge-subtree-branches.py \
--repo kernel \
--source-remote fork-kernel \
--source-branch miri/allocator \
--target-base blueos/miri/allocator \
--target-branch blueos/miri/allocator
| Parameter | Meaning |
|---|---|
--repo | Component name; its directory must match the remote prefix. |
--source-remote | Configured Josh remote name. |
--source-branch | Component branch to import. |
--target-base | Starting central commit; defaults to main. |
--target-branch | Local central branch to create or append a merge to. |
--repo-root | Central checkout; defaults to the script’s repository. |
--target-base accepts branch names, tags, full or unique abbreviated SHAs,
and Git expressions such as HEAD~1. Both branch parameters accept branch
names only. The script fixes the starting commit before merging, so
--target-base may name the target branch itself.
If the target branch does not exist, the script creates it from the starting commit. Otherwise, it appends a merge, and the starting commit must be an ancestor of the target branch or its tip. Existing target commits are preserved; the branch is not reset.
Commit tracked changes or save them with git stash before running the
script. It rejects uncommitted tracked changes and ongoing operations such
as merge or rebase. It switches branches and merges in the current working
tree, staying on the target branch after success.
Push changes back to a component branch
Skip this section if you will maintain the branch only in the central
repository. To continue maintaining a component branch, export its directory
with push-subtree-branch.py.
# Select commits between --source-base (excluded) and --source-rev (included).
#
# Add --push for an actual push, and --force for non-fast-forward updates.
python3 tools/josh/push-subtree-branch.py \
--source-base main \
--source-rev blueos/miri/allocator \
--repo build \
--target-remote fork-build \
--target-base main \
--target-branch miri/allocator \
--force
Choose the export range and component baseline
Use two central commit parameters to select a range:
--source-base: the central baseline before your changes. Exclude this commit and its ancestors.--source-rev: the final central branch tip. Include this commit.
The range is Git’s SOURCE_BASE..SOURCE_REV. For a merge branch, it also
includes merged commits reachable from the tip but not from the baseline.
Linear and merge branches therefore use the same selection model. Each run
filters one directory, including later changes to that directory made on
the final branch.
Both central parameters accept branch names, tags, full or unique abbreviated
SHAs, and expressions such as HEAD or HEAD~1. They are resolved to fixed
SHAs at the start. --source-base must be an ancestor of --source-rev or
the same commit. Equal endpoints select an empty range.
--target-base selects the component baseline branch for the replay, usually
the PR’s target branch. Use it together with --source-base. The selected
directory in the central baseline must contain the same files as the
component baseline. The baselines need not have the same name, and the
script does not choose or record the central baseline automatically.
The script replays the filtered range onto the component baseline and uses
the result to update --target-branch. Only that branch is pushed; when it
differs from --target-base, the baseline branch stays unchanged. Range mode
flattens filtered merges. If replay causes conflicts or changes the final
files compared with the source directory, the script returns an error
without pushing.
Preview and push
First preview the build export:
python3 tools/josh/push-subtree-branch.py \
--source-base main \
--source-rev blueos/miri/allocator \
--repo build \
--target-remote fork-build \
--target-base main \
--target-branch miri/allocator \
--force
To export kernel from the same final central branch, change the component and remote:
python3 tools/josh/push-subtree-branch.py \
--source-base main \
--source-rev blueos/miri/allocator \
--repo kernel \
--target-remote fork-kernel \
--target-base main \
--target-branch miri/allocator \
--force
| Parameter | Meaning |
|---|---|
--source-base | Excluded central baseline; paired with --target-base. |
--source-rev | Required final central commit; included in the range. |
--repo | Component name; uses the merge script’s directory mapping. |
--target-remote | Configured target Josh remote name. |
--target-base | Component baseline branch for the replay. |
--target-branch | Component branch to update or create. |
--repo-root | Central checkout; defaults to the script’s repository. |
--force or -f | Use Josh’s force push for non-fast-forward updates. |
--push | Push after a successful preview; omit for a dry run. |
The push script’s --target-base and --target-branch accept component branch
names only. The default is a dry run without force. Adding --force still
only previews the update. Use it when rewritten history requires a
non-fast-forward update.
In the output:
BEFORE (current remote)shows the component branch fetched before the update.AFTER (proposed, not pushed)shows the candidate that has not been pushed.
After checking the preview, add --push to the same command. For example,
force-push the build branch:
python3 tools/josh/push-subtree-branch.py \
--source-base main \
--source-rev blueos/miri/allocator \
--repo build \
--target-remote fork-build \
--target-base main \
--target-branch miri/allocator \
--force --push
Push completed appears only after an actual successful push. The script
processes committed content without moving the central development branch
or modifying the current working tree. Its detached temporary worktree is
cleaned up automatically; no temporary branch is created. Filtered results
are stored in refs/export/<remote>/<target-branch>. Range mode also uses
refs/export-bases/<remote>/<target-branch>. These helper refs are regenerated
on each invocation and do not track push progress together with the import
script.
The examples use the merge result blueos/miri/allocator. If you followed
the rebase workflow, use --source-rev blueos/miri/allocator-rebased instead.
Export the full history
To filter all history reachable from the final commit, omit both baseline
parameters and specify only --source-rev:
python3 tools/josh/push-subtree-branch.py \
--source-rev blueos/miri/allocator \
--repo build \
--target-remote fork-build \
--target-branch miri/allocator
This mode may retain central import merges and old synchronization commits. To exclude history before your changes, use range mode with explicit baselines. A SHA selects one commit; it does not by itself express the start and end of your changes.
Get updates from component repositories
josh fetch updates component remote-tracking references without moving
local development branches. If the branch has migrated and is maintained
only centrally, continue using the normal central workflow. If you still
need component branch updates, distinguish the following cases.
The remote advances normally
For a branch that still uses the original Josh import history, merge the new remote-tracking reference using ordinary Git commands.
For a development branch already integrated with the full central history, remap or transplant updates first. Directly merging the raw import reference can bring back old history. With the merge workflow, run the import script again using the current central branch as the starting point:
python3 tools/josh/merge-subtree-branches.py \
--repo build \
--source-remote fork-build \
--source-branch miri/allocator \
--target-base blueos/miri/allocator \
--target-branch blueos/miri/allocator
The script fetches, maps, and merges the source. With the rebase workflow,
identify the last imported component commit and transplant only the new
changes onto the current central branch. The current component main is
not necessarily that boundary.
The remote is rebased or force-pushed
Appending a merge preserves the old version’s history in the central branch. To remove commits deleted or rewritten on the remote, integrate the latest component branch again from a complete central baseline. Then replay the local changes and other components’ changes that you need to retain.
Keep the original branch for comparison when rebuilding. Check the commit range, resulting files, and build. This still converts component history to complete central history; a successful fetch alone does not validate the integration.
See Josh branch tools for the complete parameters and helper ref behavior of both scripts.
Appendix
Import component branches
cd blueos
josh remote add upstream-apps_example \
https://github.com/vivoblueos/apps_example.git ':prefix=apps/example'
josh remote add upstream-apps_shell \
https://github.com/vivoblueos/apps_shell.git ':prefix=apps/shell'
josh remote add upstream-book \
https://github.com/vivoblueos/book.git ':prefix=book'
josh remote add upstream-build \
https://github.com/vivoblueos/build.git ':prefix=build'
josh remote add upstream-external \
https://github.com/vivoblueos/external.git ':prefix=external'
josh remote add upstream-kernel \
https://github.com/vivoblueos/kernel.git ':prefix=kernel'
josh remote add upstream-libc \
https://github.com/vivoblueos/libc.git ':prefix=libc'
josh remote add upstream-librs \
https://github.com/vivoblueos/librs.git ':prefix=librs'
josh fetch --remote upstream-apps_example
josh fetch --remote upstream-apps_shell
josh fetch --remote upstream-book
josh fetch --remote upstream-build
josh fetch --remote upstream-external
josh fetch --remote upstream-kernel
josh fetch --remote upstream-libc
josh fetch --remote upstream-librs
josh remote add fork-apps_example \
https://github.com/{{YOUR_USERNAME}}/apps_example.git ':prefix=apps/example'
josh remote add fork-apps_shell \
https://github.com/{{YOUR_USERNAME}}/apps_shell.git ':prefix=apps/shell'
josh remote add fork-book \
https://github.com/{{YOUR_USERNAME}}/book.git ':prefix=book'
josh remote add fork-build \
https://github.com/{{YOUR_USERNAME}}/build.git ':prefix=build'
josh remote add fork-external \
https://github.com/{{YOUR_USERNAME}}/external.git ':prefix=external'
josh remote add fork-kernel \
https://github.com/{{YOUR_USERNAME}}/kernel.git ':prefix=kernel'
josh remote add fork-libc \
https://github.com/{{YOUR_USERNAME}}/libc.git ':prefix=libc'
josh remote add fork-librs \
https://github.com/{{YOUR_USERNAME}}/librs.git ':prefix=librs'
josh fetch --remote fork-apps_example
josh fetch --remote fork-apps_shell
josh fetch --remote fork-book
josh fetch --remote fork-build
josh fetch --remote fork-external
josh fetch --remote fork-kernel
josh fetch --remote fork-libc
josh fetch --remote fork-librs
git --no-pager branch --list --all
Submit pull requests
New work
Fork vivoblueos/blueos and submit a PR using the usual GitHub workflow.
Existing component PRs
For an existing component branch or unmerged PR, follow How to use the blueos repository to import the branch and integrate it with the complete central history before continuing development and publishing a central PR. Include any unmerged dependencies in the same central branch.
After publishing the replacement central PR, close the original component PR and link to the replacement to avoid merging the same work twice. If the original branch is shared, agree on the migration and where further development will take place first.