refactor(xtask): 选择架构改用参数

This commit is contained in:
YdrMaster 2022-05-13 13:33:24 +08:00
parent 3fcc1f42f3
commit 6749cd5179
8 changed files with 256 additions and 218 deletions

View File

@ -18,19 +18,19 @@ update:
# put rootfs for linux mode
rootfs:
cargo rootfs $(ARCH)
cargo rootfs --arch $(ARCH)
# put libc tests into rootfs
libc-test:
cargo libc-test $(ARCH)
cargo libc-test --arch $(ARCH)
# put other tests into rootfs
other-test:
cargo other-test $(ARCH)
cargo other-test --arch $(ARCH)
# build image from rootfs
image:
cargo image $(ARCH)
cargo image --arch $(ARCH)
# check code style
check:

View File

@ -2,6 +2,10 @@
最新的更新将出现在最上方。
## 20220513(YdrMaster)
选择架构现在是一个参数而不是子命令,例如 `cargo rootfs --arch x86_64`
## 20220512(YdrMaster)
`cargo check-style` 现在会依 CI/build 的方式工作。

View File

@ -10,7 +10,6 @@ build = "build.rs"
[dependencies]
clap = { version = "3.1", features = ["derive"] }
clap-verbosity-flag = "1.0"
dircpy = "0.3"
rand = "0.8"
shadow-rs = "0.11"

View File

@ -10,9 +10,10 @@
2. `setup`: 拉取预编译文件和子项目,补全仓库;
3. `update-all`: 更新工具链、依赖和子项目;
4. `check-style`: 编译前检查;
5. `rootfs <arch>`: 创建架构相关的 linux 启动文件系统;
6. `libc-test <arch>`: 向启动文件系统加入 libc 测试;
7. `image <arch>`: 打包指定架构的系统镜像;
5. `rootfs --arch <arch>`: 创建架构相关的 linux 启动文件系统;
6. `libc-test --arch <arch>`: 向启动文件系统加入 libc 测试;
7. `other-test --arch <arch>`: 向启动文件系统加入其他测试;
8. `image --arch <arch>`: 打包指定架构的系统镜像;
其中 6、7 执行时若 5 的结果不存在,将自动递归执行 5

View File

@ -2,7 +2,7 @@
use crate::{
command::{dir, download::wget, Cargo, CommandExt, Ext, Make, Qemu, Tar},
ALPINE_ROOTFS_VERSION, ALPINE_WEBSITE,
XError, ALPINE_ROOTFS_VERSION, ALPINE_WEBSITE,
};
use dircpy::copy_dir;
use std::{
@ -11,20 +11,28 @@ use std::{
io::Write,
os::unix,
path::{Path, PathBuf},
str::FromStr,
};
#[derive(Args)]
pub(crate) struct ArchArg {
#[clap(subcommand)]
arch: Arch,
#[derive(Clone, Copy)]
pub(crate) enum Arch {
Riscv64,
X86_64,
}
#[derive(Subcommand, Clone, Copy)]
pub(crate) enum Arch {
#[clap(name = "riscv64")]
Riscv64,
#[clap(name = "x86_64")]
X86_64,
impl FromStr for Arch {
type Err = XError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"riscv64" => Ok(Self::Riscv64),
"x86_64" => Ok(Self::X86_64),
_ => Err(XError::EnumParse {
type_name: "Arch",
value: s.into(),
}),
}
}
}
impl Arch {
@ -35,6 +43,188 @@ impl Arch {
}
}
/// 构造启动内存文件系统 rootfs。
/// 对于 x86_64这个文件系统可用于 libos 启动。
/// 若设置 `clear`,将清除已存在的目录。
pub fn make_rootfs(&self, clear: bool) {
// 若已存在且不需要清空,可以直接退出
let dir = self.rootfs();
if dir.is_dir() && !clear {
return;
}
// 下载压缩文件并解压
let src = self.prebuild_rootfs();
// 创建目标目录
dir::clear(&dir).unwrap();
fs::create_dir(dir.join("bin")).unwrap();
fs::create_dir(dir.join("lib")).unwrap();
// 拷贝 busybox
fs::copy(src.join("bin/busybox"), dir.join("bin/busybox")).unwrap();
// 拷贝 libc.so
let libc_so = format!("lib/ld-musl-{arch}.so.1", arch = self.as_str());
let so = match self {
Arch::Riscv64 => src.join(&libc_so),
Arch::X86_64 => PathBuf::from("prebuilt/linux/libc-libos.so"),
};
fs::copy(so, dir.join(libc_so)).unwrap();
// 为常用功能建立符号链接
const SH: &[&str] = &[
"cat", "cp", "echo", "false", "grep", "gzip", "kill", "ln", "ls", "mkdir", "mv",
"pidof", "ping", "ping6", "printenv", "ps", "pwd", "rm", "rmdir", "sh", "sleep",
"stat", "tar", "touch", "true", "uname", "usleep", "watch",
];
let bin = dir.join("bin");
for sh in SH {
unix::fs::symlink("busybox", bin.join(sh)).unwrap();
}
}
/// 将 libc-test 放入 rootfs。
pub fn put_libc_test(&self) {
// 递归 rootfs
self.make_rootfs(false);
// 拷贝仓库
let dir = self.libc_test();
dir::rm(&dir).unwrap();
copy_dir("libc-test", &dir).unwrap();
// 编译
fs::copy(dir.join("config.mak.def"), dir.join("config.mak")).unwrap();
match self {
Arch::Riscv64 => {
Make::new(None)
.env("ARCH", self.as_str())
.env("CROSS_COMPILE", "riscv64-linux-musl-")
.env("PATH", riscv64_linux_musl_cross())
.current_dir(&dir)
.invoke();
fs::copy(
self.target()
.join("rootfs/libc-test/functional/tls_align-static.exe"),
dir.join("src/functional/tls_align-static.exe"),
)
.unwrap();
}
Arch::X86_64 => {
fs::OpenOptions::new()
.append(true)
.open(dir.join("config.mak"))
.unwrap()
.write_all(b"CC := musl-gcc\nAR := ar\nRANLIB := ranlib")
.unwrap();
Make::new(None).current_dir(dir).invoke();
}
}
}
/// 将其他测试放入 rootfs。
pub fn put_other_test(&self) {
// 递归 rootfs
self.make_rootfs(false);
let rootfs = self.rootfs();
match self {
Arch::Riscv64 => {
let target = self.target();
copy_dir(target.join("rootfs/oscomp"), rootfs.join("oscomp")).unwrap();
}
Arch::X86_64 => {
let bin = rootfs.join("bin");
fs::read_dir("linux-syscall/test")
.unwrap()
.filter_map(|res| res.ok())
.map(|entry| entry.path())
.filter(|path| path.extension().map_or(false, |ext| ext == OsStr::new("c")))
.for_each(|c| {
Ext::new("gcc")
.arg(&c)
.arg("-o")
.arg(bin.join(c.file_prefix().unwrap()))
.arg("-Wl,--dynamic-linker=/lib/ld-musl-x86_64.so.1")
.invoke();
});
}
}
}
/// 生成镜像。
pub fn image(&self) {
// 递归 rootfs
self.make_rootfs(false);
let arch_str = self.as_str();
let image = match self {
Arch::Riscv64 => {
let rootfs = format!("rootfs/{arch_str}");
let image = format!("zCore/{arch_str}.img");
fuse(rootfs, &image);
image
}
Arch::X86_64 => {
let target = self.target();
let rootfs = self.rootfs();
fs::copy(
target.join("rootfs/lib/ld-musl-x86_64.so.1"),
rootfs.join("lib/ld-musl-x86_64.so.1"),
)
.unwrap();
let image = format!("zCore/{arch_str}.img");
fuse(&rootfs, &image);
fs::copy(
"prebuilt/linux/libc-libos.so",
rootfs.join("lib/ld-musl-x86_64.so.1"),
)
.unwrap();
image
}
};
Qemu::img()
.arg("resize")
.args(&["-f", "raw"])
.arg(image)
.arg("+5M")
.invoke();
}
/// 在 qemu 中启动。
pub fn qemu(&self) {
// 递归 image
self.image();
match self {
Arch::Riscv64 => {
Cargo::build()
.package("zcore")
.features(false, &["linux", "board-qemu"])
.target("zCore/riscv64.json")
.args(&["-Z", "build-std=core,alloc"])
.args(&["-Z", "build-std-features=compiler-builtins-mem"])
.release()
.invoke();
Ext::new("rust-objcopy")
.arg("--binary-architecture=riscv64")
.arg("target/riscv64/release/zcore")
.arg("--strip-all")
.args(&["-O", "binary", "target/riscv64/release/zcore.bin"])
.invoke();
Qemu::system(*self)
.args(&["-smp", "1"])
.args(&["-machine", "virt"])
.arg("-bios")
.arg(rustsbi_qemu())
.args(&["-m", "512M"])
.args(&["-serial", "mon:stdio"])
.args(&["-kernel", "target/riscv64/release/zcore.bin"])
.args(&["-initrd", "zCore/riscv64.img"])
.args(&["-append", "\"LOG=warn\""])
.args(&["-display", "none"])
.arg("-no-reboot")
.arg("-nographic")
.invoke();
}
Arch::X86_64 => todo!(),
}
}
fn rootfs(&self) -> PathBuf {
let mut path = PathBuf::new();
path.push("rootfs");
@ -63,213 +253,28 @@ impl Arch {
path.push(self.as_str());
path
}
}
impl ArchArg {
/// 构造启动内存文件系统 rootfs。
/// 对于 x86_64这个文件系统可用于 libos 启动。
/// 若设置 `clear`,将清除已存在的目录。
pub fn rootfs(&self, clear: bool) {
// 若已存在且不需要清空,可以直接退出
let dir = self.arch.rootfs();
if dir.is_dir() && !clear {
return;
}
// 下载压缩文件并解压
let src = self.prebuild_rootfs();
// 创建目标目录
dir::clear(&dir).unwrap();
fs::create_dir(dir.join("bin")).unwrap();
fs::create_dir(dir.join("lib")).unwrap();
// 拷贝 busybox
fs::copy(src.join("bin/busybox"), dir.join("bin/busybox")).unwrap();
// 拷贝 libc.so
let libc_so = format!("lib/ld-musl-{arch}.so.1", arch = self.arch.as_str());
let so = match self.arch {
Arch::Riscv64 => src.join(&libc_so),
Arch::X86_64 => PathBuf::from("prebuilt/linux/libc-libos.so"),
};
fs::copy(so, dir.join(libc_so)).unwrap();
// 为常用功能建立符号链接
const SH: &[&str] = &[
"cat", "cp", "echo", "false", "grep", "gzip", "kill", "ln", "ls", "mkdir", "mv",
"pidof", "ping", "ping6", "printenv", "ps", "pwd", "rm", "rmdir", "sh", "sleep",
"stat", "tar", "touch", "true", "uname", "usleep", "watch",
];
let bin = dir.join("bin");
for sh in SH {
unix::fs::symlink("busybox", bin.join(sh)).unwrap();
}
}
/// 将 libc-test 放入 rootfs。
pub fn libc_test(&self) {
// 递归 rootfs
self.rootfs(false);
// 拷贝仓库
let dir = self.arch.libc_test();
dir::rm(&dir).unwrap();
copy_dir("libc-test", &dir).unwrap();
// 编译
fs::copy(dir.join("config.mak.def"), dir.join("config.mak")).unwrap();
match self.arch {
Arch::Riscv64 => {
Make::new(None)
.env("ARCH", self.arch.as_str())
.env("CROSS_COMPILE", "riscv64-linux-musl-")
.env("PATH", riscv64_linux_musl_cross())
.current_dir(&dir)
.invoke();
fs::copy(
self.arch
.target()
.join("rootfs/libc-test/functional/tls_align-static.exe"),
dir.join("src/functional/tls_align-static.exe"),
)
.unwrap();
}
Arch::X86_64 => {
fs::OpenOptions::new()
.append(true)
.open(dir.join("config.mak"))
.unwrap()
.write_all(b"CC := musl-gcc\nAR := ar\nRANLIB := ranlib")
.unwrap();
Make::new(None).current_dir(dir).invoke();
}
}
}
/// 将其他测试放入 rootfs。
pub fn other_test(&self) {
// 递归 rootfs
self.rootfs(false);
let rootfs = self.arch.rootfs();
match self.arch {
Arch::Riscv64 => {
let target = self.arch.target();
copy_dir(target.join("rootfs/oscomp"), rootfs.join("oscomp")).unwrap();
}
Arch::X86_64 => {
let bin = rootfs.join("bin");
fs::read_dir("linux-syscall/test")
.unwrap()
.filter_map(|res| res.ok())
.map(|entry| entry.path())
.filter(|path| path.extension().map_or(false, |ext| ext == OsStr::new("c")))
.for_each(|c| {
Ext::new("gcc")
.arg(&c)
.arg("-o")
.arg(bin.join(c.file_prefix().unwrap()))
.arg("-Wl,--dynamic-linker=/lib/ld-musl-x86_64.so.1")
.invoke();
});
}
}
}
/// 生成镜像。
pub fn image(&self) {
// 递归 rootfs
self.rootfs(false);
let arch_str = self.arch.as_str();
let image = match self.arch {
Arch::Riscv64 => {
let rootfs = format!("rootfs/{arch_str}");
let image = format!("zCore/{arch_str}.img");
fuse(rootfs, &image);
image
}
Arch::X86_64 => {
let target = self.arch.target();
let rootfs = self.arch.rootfs();
fs::copy(
target.join("rootfs/lib/ld-musl-x86_64.so.1"),
rootfs.join("lib/ld-musl-x86_64.so.1"),
)
.unwrap();
let image = format!("zCore/{arch_str}.img");
fuse(&rootfs, &image);
fs::copy(
"prebuilt/linux/libc-libos.so",
rootfs.join("lib/ld-musl-x86_64.so.1"),
)
.unwrap();
image
}
};
Qemu::img()
.arg("resize")
.args(&["-f", "raw"])
.arg(image)
.arg("+5M")
.invoke();
}
/// 在 qemu 中启动。
pub fn qemu(&self) {
// 递归 image
self.image();
match self.arch {
Arch::Riscv64 => {
Cargo::build()
.package("zcore")
.features(false, &["linux", "board-qemu"])
.target("zCore/riscv64.json")
.args(&["-Z", "build-std=core,alloc"])
.args(&["-Z", "build-std-features=compiler-builtins-mem"])
.release()
.invoke();
Ext::new("rust-objcopy")
.arg("--binary-architecture=riscv64")
.arg("target/riscv64/release/zcore")
.arg("--strip-all")
.args(&["-O", "binary", "target/riscv64/release/zcore.bin"])
.invoke();
Qemu::system(self.arch)
.args(&["-smp", "1"])
.args(&["-machine", "virt"])
.arg("-bios")
.arg(rustsbi_qemu())
.args(&["-m", "512M"])
.args(&["-serial", "mon:stdio"])
.args(&["-kernel", "target/riscv64/release/zcore.bin"])
.args(&["-initrd", "zCore/riscv64.img"])
.args(&["-append", "\"LOG=warn\""])
.args(&["-display", "none"])
.arg("-no-reboot")
.arg("-nographic")
.invoke();
}
Arch::X86_64 => todo!(),
}
}
/// 下载并解压 minirootfs。
fn prebuild_rootfs(&self) -> PathBuf {
// 构造压缩文件路径
let file_name = match self.arch {
let file_name = match self {
Arch::Riscv64 => "minirootfs.tar.xz",
Arch::X86_64 => "minirootfs.tar.gz",
};
let tar = self.arch.origin().join(file_name);
let tar = self.origin().join(file_name);
// 若压缩文件不存在,需要下载
if !tar.exists() {
let url = match self.arch {
let url = match self {
Arch::Riscv64 => String::from("https://github.com/rcore-os/libc-test-prebuilt/releases/download/0.1/prebuild.tar.xz"),
Arch::X86_64 => format!("{ALPINE_WEBSITE}/x86_64/alpine-minirootfs-{ALPINE_ROOTFS_VERSION}-x86_64.tar.gz"),
};
wget(url, &tar);
}
// 解压到目标路径
let dir = self.arch.target().join("rootfs");
let dir = self.target().join("rootfs");
dir::clear(&dir).unwrap();
let mut tar = Tar::xf(&tar, Some(&dir));
match self.arch {
match self {
Arch::Riscv64 => tar.args(&["--strip-components", "1"]).invoke(),
Arch::X86_64 => tar.invoke(),
}

21
xtask/src/errors.rs Normal file
View File

@ -0,0 +1,21 @@
use std::fmt::Display;
#[derive(Debug)]
pub(crate) enum XError {
EnumParse {
type_name: &'static str,
value: String,
},
}
impl Display for XError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
XError::EnumParse { type_name, value } => {
write!(f, "Parse {type_name} from {value} failed.")
}
}
}
}
impl std::error::Error for XError {}

View File

@ -5,15 +5,17 @@
extern crate clap;
use clap::Parser;
use clap_verbosity_flag::Verbosity;
use std::{fs::read_to_string, net::Ipv4Addr};
mod arch;
mod command;
mod dump;
mod errors;
mod qemu;
use arch::ArchArg;
use arch::Arch;
use command::{Cargo, CommandExt, Ext, Git, Make};
use errors::XError;
const ALPINE_WEBSITE: &str = "https://dl-cdn.alpinelinux.org/alpine/v3.12/releases";
const ALPINE_ROOTFS_VERSION: &str = "3.12.0";
@ -27,8 +29,6 @@ struct Cli {
command: Commands,
#[clap(flatten)]
env: Env,
#[clap(flatten)]
verbose: Verbosity,
}
#[derive(Subcommand)]
@ -80,6 +80,12 @@ struct ProxyPort {
global: bool,
}
#[derive(Args)]
pub(crate) struct ArchArg {
#[clap(short, long)]
arch: Arch,
}
fn main() {
let cli = Cli::parse();
@ -101,11 +107,11 @@ fn main() {
}
Commands::UpdateAll => update_all(),
Commands::CheckStyle => check_style(),
Commands::Rootfs(arch) => arch.rootfs(true),
Commands::LibcTest(arch) => arch.libc_test(),
Commands::OtherTest(arch) => arch.other_test(),
Commands::Image(arch) => arch.image(),
Commands::Qemu(arch) => arch.qemu(),
Commands::Rootfs(arg) => arg.arch.make_rootfs(true),
Commands::LibcTest(arg) => arg.arch.put_libc_test(),
Commands::OtherTest(arg) => arg.arch.put_other_test(),
Commands::Image(arg) => arg.arch.image(),
Commands::Qemu(arg) => arg.arch.qemu(),
}
}

2
xtask/src/qemu.rs Normal file
View File

@ -0,0 +1,2 @@
#[derive(Args)]
pub(crate) struct QemuhArgs {}