mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 12:56:48 +00:00
Add OSDK command line interface
This commit is contained in:
parent
f2f991b239
commit
3b3d088767
25
.github/workflows/osdk_ci.yml
vendored
Normal file
25
.github/workflows/osdk_ci.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: OSDK CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
osdk-ci:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
container: asterinas/asterinas:0.3.0
|
||||
steps:
|
||||
- run: echo "Running in asterinas/asterinas:0.3.0"
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Format check
|
||||
id: fmt_check
|
||||
run: cd osdk && cargo check && cargo clippy
|
||||
|
||||
- name: Unit test
|
||||
id: unit_test
|
||||
run: cd osdk && cargo build && cargo test
|
98
Cargo.lock
generated
98
Cargo.lock
generated
@ -49,9 +49,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.4"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@ -63,9 +63,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
@ -427,7 +427,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -450,9 +450,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.6"
|
||||
version = "4.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
||||
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -460,33 +460,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.6"
|
||||
version = "4.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
|
||||
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
"strsim 0.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.2"
|
||||
version = "4.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
|
||||
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.1"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
@ -596,7 +596,7 @@ dependencies = [
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"strsim 0.10.0",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
@ -631,7 +631,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -750,7 +750,7 @@ checksum = "ba330b70a5341d3bc730b8e205aaee97ddab5d9c448c4f51a7c2d924266fa8f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -779,9 +779,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
@ -810,12 +810,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.0"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.0",
|
||||
"hashbrown 0.14.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -842,7 +842,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -897,7 +897,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rand",
|
||||
"syn 2.0.29",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -936,9 +936,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "libflate"
|
||||
@ -1039,9 +1039,9 @@ checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@ -1166,9 +1166,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -1195,9 +1195,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -1300,29 +1300,29 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.192"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.192"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
|
||||
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -1384,6 +1384,12 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@ -1397,9 +1403,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.29"
|
||||
version = "2.0.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
|
||||
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1439,7 +1445,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1473,9 +1479,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.3"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@ -1562,7 +1568,7 @@ checksum = "26a7b1c2c808c3db854a54d5215e3f7e7aaf5dcfbce095598cba6af29895695d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
"syn 2.0.49",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -60,6 +60,7 @@ members = [
|
||||
]
|
||||
|
||||
exclude = [
|
||||
"osdk",
|
||||
"services/libs/comp-sys/cargo-component",
|
||||
"services/libs/comp-sys/component",
|
||||
"services/libs/comp-sys/component-macro",
|
||||
|
10
osdk/.gitignore
vendored
Normal file
10
osdk/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
591
osdk/Cargo.lock
generated
Normal file
591
osdk/Cargo.lock
generated
Normal file
@ -0,0 +1,591 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00ad3f3a942eee60335ab4342358c161ee296829e0d16ff42fc1d6cb07815467"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"bstr",
|
||||
"doc-comment",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
"predicates-tree",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-osdk"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80932e03c33999b9235edb8655bc9df3204adc9887c2f95b50cb1deb9fd54253"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6c0db58c659eef1c73e444d298c27322a1b52f6927d2ad470c0c0f96fa7b8fa"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9eeb342678d785662fd2514be38c459bb925f02b68dd2a3e0f21d7ef82d979dd"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"humantime",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"difflib",
|
||||
"predicates-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates-core"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
|
||||
|
||||
[[package]]
|
||||
name = "predicates-tree"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
|
||||
dependencies = [
|
||||
"predicates-core",
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
24
osdk/Cargo.toml
Normal file
24
osdk/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "cargo-osdk"
|
||||
version = "0.1.5"
|
||||
edition = "2021"
|
||||
description = "Accelerate OS development with Asterinas OSDK"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.4.17", features = ["cargo", "derive"] }
|
||||
env_logger = "0.11.0"
|
||||
indexmap = "2.2.1"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.20"
|
||||
regex = "1.10.3"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
serde_json = "1.0.111"
|
||||
toml = { version = "0.8.8", features = ["preserve_order"] }
|
||||
which = "6.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0.13"
|
145
osdk/README.md
Normal file
145
osdk/README.md
Normal file
@ -0,0 +1,145 @@
|
||||
# Accelerate OS development with Asterinas OSDK
|
||||
|
||||
[](https://crates.io/crates/cargo-osdk)
|
||||
|
||||
### What is it?
|
||||
|
||||
OSDK (short for Operating System Development Kit) is designed to simplify the development of Rust operating systems. It aims to streamline the process by leveraging the framekernel architecture, originally proposed by [Asterinas](https://github.com/asterinas/asterinas).
|
||||
|
||||
`cargo-osdk` is a command-line tool that facilitates project management for those developed on the framekernel architecture. Much like Cargo for Rust projects, `cargo-osdk` enables building, running, and testing projects conveniently.
|
||||
|
||||
### Install the tool
|
||||
|
||||
#### Requirements
|
||||
|
||||
Currenly, `cargo-osdk` only supports x86_64 ubuntu system.
|
||||
|
||||
To run a kernel with QEMU, `cargo-osdk` requires the following tools to be installed:
|
||||
- Rust >= 1.75.0
|
||||
- gcc
|
||||
- qemu-system-x86_64
|
||||
- grub-mkrescue
|
||||
- ovmf
|
||||
- xorriso
|
||||
|
||||
About how to install Rust, you can refer to the [official site](https://www.rust-lang.org/tools/install).
|
||||
|
||||
Other tools can be installed by
|
||||
```bash
|
||||
apt install build-essential grub2-common qemu-system-x86 ovmf xorriso
|
||||
```
|
||||
|
||||
#### Install
|
||||
|
||||
Then, `cargo-osdk` can be installed by
|
||||
```bash
|
||||
cargo install cargo-osdk
|
||||
```
|
||||
|
||||
#### Upgrade
|
||||
|
||||
If `cargo-osdk` is already installed, the tool can be upgraded by
|
||||
```bash
|
||||
cargo install --force cargo-osdk
|
||||
```
|
||||
|
||||
### Get start
|
||||
|
||||
Here we provide a simple demo to demonstrate how to create and run a simple kernel with `cargo-osdk`.
|
||||
|
||||
With `cargo-osdk`, a kernel project can be created by one command
|
||||
```bash
|
||||
cargo osdk new --kernel my-first-os
|
||||
```
|
||||
|
||||
Then, you can run the kernel with
|
||||
```bash
|
||||
cd my-first-os && cargo osdk run
|
||||
```
|
||||
|
||||
You will see `Hello world from guest kernel!` from your console.
|
||||
|
||||
### Basic usage
|
||||
|
||||
The basic usage of `cargo-osdk` is
|
||||
```bash
|
||||
cargo osdk <COMMAND>
|
||||
```
|
||||
Currently we support following commands:
|
||||
- **new**: Create a new kernel package or library package
|
||||
- **build**: Compile the project and its dependencies
|
||||
- **run**: Run the kernel with a VMM
|
||||
- **test**: Execute kernel mode unit test by starting a VMM
|
||||
- **check**: Analyze the current package and report errors
|
||||
- **clippy**: Check the current package and catch common mistakes
|
||||
|
||||
The following command can be used to discover the available options for each command.
|
||||
```bash
|
||||
cargo osdk help <COMMAND>
|
||||
```
|
||||
|
||||
### The configuration file
|
||||
|
||||
`cargo-osdk` utilizes a configuration file to define its precise behavior. Typically, the configuration file is named `OSDK.toml` and is placed in the root directory of the workspace (the same directory as the workspace's `Cargo.toml`). If there is only one crate and no workspace, the file is placed in the crate's root directory. Below, you will find a comprehensive version of the available configuration options.
|
||||
|
||||
```toml
|
||||
kcmd_args = ["init=/bin/busybox", "path=/usr/local/bin"] # <1>
|
||||
init_args = ["sh", "-l"] # <2>
|
||||
initramfs="./build/initramfs.cpio.gz" # <3>
|
||||
|
||||
[boot]
|
||||
loader = "grub" # <4>
|
||||
protocol = "multiboot2" # <5>
|
||||
grub-mkrescue = "/usr/bin/grub-mkrescue" # <6>
|
||||
ovmf = "/usr/bin/ovmf" # <7>
|
||||
|
||||
[qemu]
|
||||
path = "/usr/bin/qemu-system-x86_64" # <8>
|
||||
machine = "q35" # <9>
|
||||
args = [ # <10>
|
||||
"-enable-kvm",
|
||||
"-m 2G",
|
||||
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off"
|
||||
]
|
||||
|
||||
[qemu.'cfg(feature="iommu")'] # <11>
|
||||
path = "/usr/local/sbin/qemu-kvm" # <8>
|
||||
machine = "q35" # <9>
|
||||
args = [ # <10>
|
||||
"-enable-kvm",
|
||||
"-m 2G",
|
||||
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on",
|
||||
"-device intel-iommu,intremap=on,device-iotlb=on"
|
||||
]
|
||||
```
|
||||
|
||||
1. The arguments provided will be passed to the guest kernel.
|
||||
Optional. The default value is empty.
|
||||
Each argument should be in one of the following two forms: `KEY=VALUE` or `KEY` if no value is required. Each `KEY` can appear at most once.
|
||||
2. The arguments provided will be passed to the init process, usually, the init shell.
|
||||
Optional. The default value is empty.
|
||||
3. The path to the built initramfs.
|
||||
Optional. The default value is empty.
|
||||
4. The bootloader used to boot the kernel.
|
||||
Optional. The default value is `grub`.
|
||||
The allowed values are `grub` and `qemu` (`qemu` indicates that QEMU directly boots the kernel).
|
||||
5. The boot protocol used to boot the kernel.
|
||||
Optional. The default value is `multiboot2`.
|
||||
The allowed values are `linux-efi-handover64`, `linux-legacy32`, `multiboot`, and `multiboot2`.
|
||||
6. The path of `grub-mkrescue`, which is used to create a GRUB CD_ROM.
|
||||
Optional. The default value is system path, determined using `which grub-mkrescue`.
|
||||
This argument only takes effect when the bootloader is `grub`.
|
||||
7. The path of OVMF. OVMF enables UEFI support for QEMU.
|
||||
Optional. The default value is empty.
|
||||
This argument only takes effect when the boot protocol is `linux-efi-handover64`.
|
||||
8. The path of QEMU.
|
||||
Optional. The default value is system path, determined using `which qemu-system-x86_64`.
|
||||
9. The machine type of QEMU.
|
||||
Optional. Default is `q35`.
|
||||
The allowed values are `q35` and `microvm`.
|
||||
10. Additional arguments passed to QEMU.
|
||||
Optional. The default value is empty.
|
||||
Each argument should be in the form `KEY VALUE` (separated by space), or `KEY` if no value is required. Some keys can appear multiple times (e.g., `-device`, `-netdev`), while other keys can appear at most once. Certain keys, such as `-cpu` and `-machine`, are not allowed to be set here as they may conflict with the internal settings of `cargo-osdk`.
|
||||
11. Conditional QEMU settings.
|
||||
Optional. The default value is empty.
|
||||
Conditional QEMU settings allow for a condition to be specified after `qemu`. Currently, `cargo-osdk` only supports the condition `cfg(feature="FEATURE")`, which activates the QEMU settings only if the `FEATURE` is set. The `FEATURE` must be defined in the project's `Cargo.toml`. At most one conditional setting can be activated at a time. If multiple conditional settings can be activated simultaneously, `cargo-osdk` will report an error. In the future, `cargo-osdk` will support all possible conditions that [Rust conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html) supports.
|
175
osdk/src/cli.rs
Normal file
175
osdk/src/cli.rs
Normal file
@ -0,0 +1,175 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{crate_version, Args, Parser};
|
||||
|
||||
use crate::commands::{execute_check_command, execute_clippy_command, execute_new_command};
|
||||
use crate::config_manager::boot::{BootLoader, BootProtocol};
|
||||
use crate::config_manager::qemu::QemuMachine;
|
||||
use crate::config_manager::{BuildConfig, RunConfig, TestConfig};
|
||||
|
||||
pub fn main() {
|
||||
let osdk_subcommand = match Cli::parse() {
|
||||
Cli {
|
||||
cargo_subcommand: CargoSubcommand::Osdk(osdk_subcommand),
|
||||
} => osdk_subcommand,
|
||||
};
|
||||
|
||||
match &osdk_subcommand {
|
||||
OsdkSubcommand::New(args) => execute_new_command(args),
|
||||
OsdkSubcommand::Build(build_args) => {
|
||||
let build_config = BuildConfig::parse(build_args);
|
||||
println!("{:?}", build_config);
|
||||
// TODO: execute_build_command(build_config);
|
||||
// todo!("execute build command");
|
||||
}
|
||||
OsdkSubcommand::Run(run_args) => {
|
||||
let run_config = RunConfig::parse(run_args);
|
||||
println!("{:?}", run_config);
|
||||
// TODO: execute_run_command(run_config);
|
||||
// todo!("execute run command");
|
||||
}
|
||||
OsdkSubcommand::Test(test_args) => {
|
||||
let test_config = TestConfig::parse(test_args);
|
||||
println!("{:?}", test_config);
|
||||
// TODO: execute_test_command(test_config);
|
||||
// todo!("execute test command");
|
||||
}
|
||||
OsdkSubcommand::Check => execute_check_command(),
|
||||
OsdkSubcommand::Clippy => execute_clippy_command(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(display_name = "cargo", bin_name = "cargo")]
|
||||
/// Project Manager for the crates developed based on frame kernel
|
||||
pub struct Cli {
|
||||
#[clap(subcommand)]
|
||||
cargo_subcommand: CargoSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
enum CargoSubcommand {
|
||||
#[clap(subcommand, version = crate_version!())]
|
||||
Osdk(OsdkSubcommand),
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub enum OsdkSubcommand {
|
||||
#[command(
|
||||
about = "Create a new kernel package or library package which depends on aster-frame"
|
||||
)]
|
||||
New(NewArgs),
|
||||
#[command(about = "Compile the project and its dependencies")]
|
||||
Build(BuildArgs),
|
||||
#[command(about = "Run the kernel with a VMM")]
|
||||
Run(RunArgs),
|
||||
#[command(about = "Execute kernel mode unit test by starting a VMM")]
|
||||
Test(TestArgs),
|
||||
#[command(about = "Analyze the current package and report errors")]
|
||||
Check,
|
||||
#[command(about = "Check the current package and catch common mistakes")]
|
||||
Clippy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct NewArgs {
|
||||
#[arg(long, default_value = "false", help = "Use the kernel template")]
|
||||
pub kernel: bool,
|
||||
#[arg(name = "name", required = true)]
|
||||
pub crate_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct BuildArgs {
|
||||
#[command(flatten)]
|
||||
pub cargo_args: CargoArgs,
|
||||
#[command(flatten)]
|
||||
pub osdk_args: OsdkArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct RunArgs {
|
||||
#[command(flatten)]
|
||||
pub cargo_args: CargoArgs,
|
||||
#[command(flatten)]
|
||||
pub osdk_args: OsdkArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct TestArgs {
|
||||
#[command(flatten)]
|
||||
pub cargo_args: CargoArgs,
|
||||
#[arg(
|
||||
name = "TESTNAME",
|
||||
help = "Only run tests containing this string in their names"
|
||||
)]
|
||||
pub test_name: Option<String>,
|
||||
#[command(flatten)]
|
||||
pub osdk_args: OsdkArgs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args, Default)]
|
||||
pub struct CargoArgs {
|
||||
#[arg(
|
||||
long,
|
||||
help = "Build artifacts in release mode",
|
||||
default_value = "false"
|
||||
)]
|
||||
pub release: bool,
|
||||
#[arg(long, value_name = "FEATURES", help = "List of features to activate")]
|
||||
pub features: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct OsdkArgs {
|
||||
#[arg(
|
||||
long = "kcmd_args",
|
||||
help = "Command line arguments for guest kernel",
|
||||
value_name = "ARGS"
|
||||
)]
|
||||
pub kcmd_args: Vec<String>,
|
||||
#[arg(
|
||||
long = "init_args",
|
||||
help = "Command line arguments for init process",
|
||||
value_name = "ARGS"
|
||||
)]
|
||||
pub init_args: Vec<String>,
|
||||
#[arg(long, help = "Path of initramfs", value_name = "PATH")]
|
||||
pub initramfs: Option<PathBuf>,
|
||||
#[arg(long = "boot.ovmf", help = "Path of OVMF", value_name = "PATH")]
|
||||
pub boot_ovmf: Option<PathBuf>,
|
||||
#[arg(
|
||||
long = "boot.loader",
|
||||
help = "Loader for booting the kernel",
|
||||
value_name = "LOADER"
|
||||
)]
|
||||
pub boot_loader: Option<BootLoader>,
|
||||
#[arg(
|
||||
long = "boot.grub-mkrescue",
|
||||
help = "Path of grub-mkrescue",
|
||||
value_name = "PATH"
|
||||
)]
|
||||
pub boot_grub_mkrescue: Option<PathBuf>,
|
||||
#[arg(
|
||||
long = "boot.protocol",
|
||||
help = "Protocol for booting the kernel",
|
||||
value_name = "PROTOCOL"
|
||||
)]
|
||||
pub boot_protocol: Option<BootProtocol>,
|
||||
#[arg(long = "qemu.path", help = "Path of QEMU", value_name = "PATH")]
|
||||
pub qemu_path: Option<PathBuf>,
|
||||
#[arg(
|
||||
long = "qemu.machine",
|
||||
help = "QEMU machine type",
|
||||
value_name = "MACHINE"
|
||||
)]
|
||||
pub qemu_machine: Option<QemuMachine>,
|
||||
#[arg(
|
||||
long = "qemu.args",
|
||||
help = "Arguments for running QEMU",
|
||||
value_name = "ARGS"
|
||||
)]
|
||||
pub qemu_args: Vec<String>,
|
||||
}
|
27
osdk/src/commands/check.rs
Normal file
27
osdk/src/commands/check.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::process;
|
||||
|
||||
use crate::commands::utils::create_target_json;
|
||||
use crate::error::Errno;
|
||||
use crate::error_msg;
|
||||
use crate::utils::get_cargo_metadata;
|
||||
|
||||
use super::utils::{cargo, COMMON_CARGO_ARGS};
|
||||
|
||||
pub fn execute_check_command() {
|
||||
let target_json_path = {
|
||||
let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>);
|
||||
let target_directory = metadata.get("target_directory").unwrap().as_str().unwrap();
|
||||
create_target_json(target_directory)
|
||||
};
|
||||
|
||||
let mut command = cargo();
|
||||
command.arg("check").arg("--target").arg(target_json_path);
|
||||
command.args(COMMON_CARGO_ARGS);
|
||||
let status = command.status().unwrap();
|
||||
if !status.success() {
|
||||
error_msg!("Check failed");
|
||||
process::exit(Errno::ExecuteCommand as _);
|
||||
}
|
||||
}
|
38
osdk/src/commands/clippy.rs
Normal file
38
osdk/src/commands/clippy.rs
Normal file
@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::process;
|
||||
|
||||
use crate::error_msg;
|
||||
use crate::utils::get_cargo_metadata;
|
||||
use crate::{commands::utils::create_target_json, error::Errno};
|
||||
|
||||
use super::utils::{cargo, COMMON_CARGO_ARGS};
|
||||
|
||||
pub fn execute_clippy_command() {
|
||||
let target_json_path = {
|
||||
let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>);
|
||||
let target_directory = metadata.get("target_directory").unwrap().as_str().unwrap();
|
||||
create_target_json(target_directory)
|
||||
};
|
||||
|
||||
let mut command = cargo();
|
||||
command.arg("clippy").arg("-h");
|
||||
info!("[Running] cargo clippy -h");
|
||||
let output = command.output().unwrap();
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
eprintln!("{}", &stderr);
|
||||
error_msg!("Cargo clippy failed");
|
||||
process::exit(Errno::ExecuteCommand as _);
|
||||
}
|
||||
|
||||
let mut command = cargo();
|
||||
command.arg("clippy").arg("--target").arg(target_json_path);
|
||||
command.args(COMMON_CARGO_ARGS);
|
||||
command.args(["--", "-D", "warnings"]);
|
||||
let status = command.status().unwrap();
|
||||
if !status.success() {
|
||||
error_msg!("Cargo clippy failed");
|
||||
process::exit(Errno::ExecuteCommand as _);
|
||||
}
|
||||
}
|
12
osdk/src/commands/mod.rs
Normal file
12
osdk/src/commands/mod.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! This module contains subcommands of cargo-osdk.
|
||||
|
||||
mod check;
|
||||
mod clippy;
|
||||
mod new;
|
||||
mod utils;
|
||||
|
||||
pub use self::check::execute_check_command;
|
||||
pub use self::clippy::execute_clippy_command;
|
||||
pub use self::new::execute_new_command;
|
164
osdk/src/commands/new.rs
Normal file
164
osdk/src/commands/new.rs
Normal file
@ -0,0 +1,164 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::{fs, process};
|
||||
|
||||
use crate::cli::NewArgs;
|
||||
use crate::error::Errno;
|
||||
use crate::error_msg;
|
||||
use crate::utils::{cargo_new_lib, get_cargo_metadata, ASTER_FRAME_DEP};
|
||||
|
||||
pub fn execute_new_command(args: &NewArgs) {
|
||||
cargo_new_lib(&args.crate_name);
|
||||
let cargo_metadata = get_cargo_metadata(Some(&args.crate_name), None::<&[&str]>);
|
||||
add_manifest_dependencies(&cargo_metadata, &args.crate_name);
|
||||
create_osdk_manifest(&cargo_metadata);
|
||||
if args.kernel {
|
||||
write_kernel_template(&cargo_metadata, &args.crate_name);
|
||||
} else {
|
||||
write_library_template(&cargo_metadata, &args.crate_name);
|
||||
}
|
||||
add_rust_toolchain(&cargo_metadata);
|
||||
}
|
||||
|
||||
fn add_manifest_dependencies(cargo_metadata: &serde_json::Value, crate_name: &str) {
|
||||
let mainfest_path = get_manifest_path(cargo_metadata, crate_name);
|
||||
|
||||
let mut manifest: toml::Table = {
|
||||
let content = fs::read_to_string(mainfest_path).unwrap();
|
||||
toml::from_str(&content).unwrap()
|
||||
};
|
||||
|
||||
let dependencies = manifest.get_mut("dependencies").unwrap();
|
||||
|
||||
let aster_frame_dep = toml::Table::from_str(ASTER_FRAME_DEP).unwrap();
|
||||
dependencies.as_table_mut().unwrap().extend(aster_frame_dep);
|
||||
|
||||
let content = toml::to_string(&manifest).unwrap();
|
||||
fs::write(mainfest_path, content).unwrap();
|
||||
}
|
||||
|
||||
fn create_osdk_manifest(cargo_metadata: &serde_json::Value) {
|
||||
let osdk_manifest_path = {
|
||||
let workspace_root = get_workspace_root(cargo_metadata);
|
||||
PathBuf::from(workspace_root).join("OSDK.toml")
|
||||
};
|
||||
|
||||
if osdk_manifest_path.is_file() {
|
||||
// If the OSDK.toml of workspace exists, just return.
|
||||
return;
|
||||
}
|
||||
|
||||
// Create `OSDK.toml` for the workspace
|
||||
fs::write(osdk_manifest_path, "").unwrap();
|
||||
}
|
||||
|
||||
/// Write the default content of `src/kernel.rs`, with contents in provided template.
|
||||
fn write_kernel_template(cargo_metadata: &serde_json::Value, crate_name: &str) {
|
||||
let src_path = get_src_path(cargo_metadata, crate_name);
|
||||
let contents = include_str!("template/kernel.template");
|
||||
fs::write(src_path, contents).unwrap();
|
||||
}
|
||||
|
||||
/// Write the default content of `src/lib.rs`, with contents in provided template.
|
||||
fn write_library_template(cargo_metadata: &serde_json::Value, crate_name: &str) {
|
||||
let src_path = get_src_path(cargo_metadata, crate_name);
|
||||
let contents = include_str!("template/lib.template");
|
||||
fs::write(src_path, contents).unwrap();
|
||||
}
|
||||
|
||||
/// Add the rust-toolchain.toml file in workspace root
|
||||
fn add_rust_toolchain(cargo_metadata: &serde_json::Value) {
|
||||
let workspace_root = get_workspace_root(cargo_metadata);
|
||||
|
||||
let rust_toolchain_path = PathBuf::from(workspace_root).join("rust-toolchain.toml");
|
||||
if let Ok(true) = rust_toolchain_path.try_exists() {
|
||||
let toolchain = {
|
||||
let content = fs::read_to_string(&rust_toolchain_path).unwrap();
|
||||
toml::Table::from_str(&content).unwrap()
|
||||
};
|
||||
|
||||
check_rust_toolchain(&toolchain);
|
||||
return;
|
||||
}
|
||||
|
||||
let contents = include_str!("template/rust-toolchain.toml.template");
|
||||
fs::write(rust_toolchain_path, contents).unwrap();
|
||||
}
|
||||
|
||||
fn get_manifest_path<'a>(cargo_metadata: &'a serde_json::Value, crate_name: &str) -> &'a str {
|
||||
let metadata = get_package_metadata(cargo_metadata, crate_name);
|
||||
metadata.get("manifest_path").unwrap().as_str().unwrap()
|
||||
}
|
||||
|
||||
fn get_src_path<'a>(cargo_metadata: &'a serde_json::Value, crate_name: &str) -> &'a str {
|
||||
let metadata = get_package_metadata(cargo_metadata, crate_name);
|
||||
let targets = metadata.get("targets").unwrap().as_array().unwrap();
|
||||
|
||||
for target in targets {
|
||||
let name = target.get("name").unwrap().as_str().unwrap();
|
||||
if name != crate_name {
|
||||
continue;
|
||||
}
|
||||
|
||||
let src_path = target.get("src_path").unwrap();
|
||||
return src_path.as_str().unwrap();
|
||||
}
|
||||
|
||||
panic!("the crate name does not match with any target");
|
||||
}
|
||||
|
||||
fn get_workspace_root(cargo_metadata: &serde_json::Value) -> &str {
|
||||
let workspace_root = cargo_metadata.get("workspace_root").unwrap();
|
||||
workspace_root.as_str().unwrap()
|
||||
}
|
||||
|
||||
fn get_package_metadata<'a>(
|
||||
cargo_metadata: &'a serde_json::Value,
|
||||
crate_name: &str,
|
||||
) -> &'a serde_json::Value {
|
||||
let packages = cargo_metadata.get("packages").unwrap();
|
||||
|
||||
let package_metadatas = packages.as_array().unwrap();
|
||||
|
||||
for package_metadata in package_metadatas {
|
||||
let name = package_metadata.get("name").unwrap().as_str().unwrap();
|
||||
if crate_name == name {
|
||||
return package_metadata;
|
||||
}
|
||||
}
|
||||
|
||||
panic!("cannot find metadata of the crate")
|
||||
}
|
||||
|
||||
fn check_rust_toolchain(toolchain: &toml::Table) {
|
||||
let expected = {
|
||||
let contents = include_str!("template/rust-toolchain.toml.template");
|
||||
toml::Table::from_str(contents).unwrap()
|
||||
};
|
||||
|
||||
let expected = expected.get("toolchain").unwrap().as_table().unwrap();
|
||||
let toolchain = toolchain.get("toolchain").unwrap().as_table().unwrap();
|
||||
|
||||
let channel = toolchain.get("channel").unwrap().as_str().unwrap();
|
||||
let expected_channel = expected.get("channel").unwrap().as_str().unwrap();
|
||||
|
||||
if channel != expected_channel {
|
||||
error_msg!("The current version of rust-toolchain.toml is not compatible with the osdk");
|
||||
process::exit(Errno::AddRustToolchain as _);
|
||||
}
|
||||
|
||||
let components = toolchain.get("components").unwrap().as_array().unwrap();
|
||||
let expected_components = toolchain.get("components").unwrap().as_array().unwrap();
|
||||
|
||||
for expected_component in expected_components {
|
||||
if !components.contains(expected_component) {
|
||||
error_msg!(
|
||||
"rust-toolchain.toml does not contains {}",
|
||||
expected_component.as_str().unwrap()
|
||||
);
|
||||
process::exit(Errno::AddRustToolchain as _);
|
||||
}
|
||||
}
|
||||
}
|
9
osdk/src/commands/template/kernel.template
Normal file
9
osdk/src/commands/template/kernel.template
Normal file
@ -0,0 +1,9 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use aster_frame::prelude::*;
|
||||
|
||||
#[aster_main]
|
||||
fn kernel_main() {
|
||||
println!("Hello world from guest kernel!");
|
||||
}
|
10
osdk/src/commands/template/lib.template
Normal file
10
osdk/src/commands/template/lib.template
Normal file
@ -0,0 +1,10 @@
|
||||
#![no_std]
|
||||
|
||||
#[cfg(ktest)]
|
||||
mod tests {
|
||||
#[ktest]
|
||||
fn it_works() {
|
||||
let memory_regions = aster_frame::boot::memory_regions();
|
||||
assert!(!memory_regions.is_empty());
|
||||
}
|
||||
}
|
3
osdk/src/commands/template/rust-toolchain.toml.template
Normal file
3
osdk/src/commands/template/rust-toolchain.toml.template
Normal file
@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2023-12-01"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
|
16
osdk/src/commands/template/x86_64-custom.json.template
Normal file
16
osdk/src/commands/template/x86_64-custom.json.template
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"llvm-target": "x86_64-unknown-none",
|
||||
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
|
||||
"code-model": "kernel",
|
||||
"cpu": "x86-64",
|
||||
"arch": "x86_64",
|
||||
"target-endian": "little",
|
||||
"target-pointer-width": "64",
|
||||
"target-c-int-width": "32",
|
||||
"os": "none",
|
||||
"executables": true,
|
||||
"linker-flavor": "ld.lld",
|
||||
"linker": "rust-lld",
|
||||
"disable-redzone": true,
|
||||
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
|
||||
}
|
29
osdk/src/commands/utils.rs
Normal file
29
osdk/src/commands/utils.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
pub const COMMON_CARGO_ARGS: &[&str] = &[
|
||||
"-Zbuild-std=core,alloc,compiler_builtins",
|
||||
"-Zbuild-std-features=compiler-builtins-mem",
|
||||
];
|
||||
|
||||
pub fn cargo() -> Command {
|
||||
Command::new("cargo")
|
||||
}
|
||||
|
||||
pub fn create_target_json(target_directory: impl AsRef<Path>) -> PathBuf {
|
||||
let target_osdk_dir = PathBuf::from(target_directory.as_ref()).join("osdk");
|
||||
fs::create_dir_all(&target_osdk_dir).unwrap();
|
||||
|
||||
let target_json_path = target_osdk_dir.join("x86_64-custom.json");
|
||||
if target_json_path.is_file() {
|
||||
return target_json_path;
|
||||
}
|
||||
|
||||
let contents = include_str!("template/x86_64-custom.json.template");
|
||||
fs::write(&target_json_path, contents).unwrap();
|
||||
|
||||
target_json_path
|
||||
}
|
67
osdk/src/config_manager/boot.rs
Normal file
67
osdk/src/config_manager/boot.rs
Normal file
@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
|
||||
use crate::error::Errno;
|
||||
use crate::error_msg;
|
||||
|
||||
/// Arguments for creating bootdev image and how to boot with vmm.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Boot {
|
||||
#[serde(default)]
|
||||
pub loader: BootLoader,
|
||||
#[serde(default)]
|
||||
pub protocol: BootProtocol,
|
||||
/// The path of `grub_mkrecue`. Only be `Some(_)` if `loader` is `BootLoader::grub`.
|
||||
pub grub_mkrescue: Option<PathBuf>,
|
||||
/// The path of ovmf. Only be `Some(_)` if `protocol` is `BootProtocol::LinuxEfiHandover64`.
|
||||
pub ovmf: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum BootLoader {
|
||||
#[default]
|
||||
Grub,
|
||||
Qemu,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for BootLoader {
|
||||
fn from(value: &'a str) -> Self {
|
||||
match value {
|
||||
"grub" => Self::Grub,
|
||||
"qemu" => Self::Qemu,
|
||||
_ => {
|
||||
error_msg!("`{}` is not a valid option for `boot.loader`. Allowed options are `grub` and `qemu`.",value);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum BootProtocol {
|
||||
LinuxEfiHandover64,
|
||||
LinuxLegacy32,
|
||||
Multiboot,
|
||||
#[default]
|
||||
Multiboot2,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for BootProtocol {
|
||||
fn from(value: &'a str) -> Self {
|
||||
match value {
|
||||
"linux-efi-handover64" => Self::LinuxEfiHandover64,
|
||||
"linux-legacy32" => Self::LinuxLegacy32,
|
||||
"multiboot" => Self::Multiboot,
|
||||
"multiboot2" => Self::Multiboot2,
|
||||
_ => {
|
||||
error_msg!("`{}` is not a valid option for `boot.protocol`. Allowed options are `linux-efi-handover64`, `linux-legacy32`, `multiboot`, `multiboot2`", value);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
osdk/src/config_manager/manifest.rs
Normal file
123
osdk/src/config_manager/manifest.rs
Normal file
@ -0,0 +1,123 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::error::Errno;
|
||||
use crate::error_msg;
|
||||
|
||||
use super::boot::Boot;
|
||||
use super::qemu::{CfgQemu, Qemu};
|
||||
|
||||
/// The osdk manifest from configuration file and command line arguments.
|
||||
#[derive(Debug)]
|
||||
pub struct OsdkManifest {
|
||||
pub kcmd_args: Vec<String>,
|
||||
pub initramfs: Option<PathBuf>,
|
||||
pub boot: Boot,
|
||||
pub qemu: Qemu,
|
||||
}
|
||||
|
||||
impl OsdkManifest {
|
||||
pub fn from_toml_manifest<S: AsRef<str>>(toml_manifest: TomlManifest, features: &[S]) -> Self {
|
||||
let TomlManifest {
|
||||
mut kcmd_args,
|
||||
mut init_args,
|
||||
initramfs,
|
||||
boot,
|
||||
qemu,
|
||||
} = toml_manifest;
|
||||
let CfgQemu { default, cfg } = qemu;
|
||||
|
||||
let Some(cfg) = cfg else {
|
||||
return Self {
|
||||
kcmd_args,
|
||||
initramfs,
|
||||
boot,
|
||||
qemu: default,
|
||||
};
|
||||
};
|
||||
|
||||
for cfg in cfg.keys() {
|
||||
check_cfg(cfg);
|
||||
}
|
||||
|
||||
let mut qemu_args = None;
|
||||
|
||||
let mut feature_enabled_args: Vec<_> = cfg
|
||||
.into_iter()
|
||||
.filter_map(|(cfg, args)| {
|
||||
if features
|
||||
.iter()
|
||||
.any(|feature| cfg.contains(feature.as_ref()))
|
||||
{
|
||||
Some(args)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if feature_enabled_args.len() > 1 {
|
||||
error_msg!("Multiple features are conflict");
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
} else if feature_enabled_args.len() == 1 {
|
||||
qemu_args = Some(feature_enabled_args.remove(0));
|
||||
} else if feature_enabled_args.is_empty() {
|
||||
qemu_args = Some(default);
|
||||
}
|
||||
|
||||
check_args("kcmd_args", &kcmd_args);
|
||||
check_args("init_args", &init_args);
|
||||
|
||||
kcmd_args.push("--".to_string());
|
||||
kcmd_args.append(&mut init_args);
|
||||
|
||||
OsdkManifest {
|
||||
kcmd_args,
|
||||
initramfs,
|
||||
boot,
|
||||
qemu: qemu_args.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The osdk manifest from configuration file `OSDK.toml`.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct TomlManifest {
|
||||
/// Command line arguments for guest kernel
|
||||
#[serde(default)]
|
||||
pub kcmd_args: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub init_args: Vec<String>,
|
||||
/// The path of initramfs
|
||||
pub initramfs: Option<PathBuf>,
|
||||
#[serde(default)]
|
||||
pub boot: Boot,
|
||||
#[serde(default)]
|
||||
pub qemu: CfgQemu,
|
||||
}
|
||||
|
||||
fn check_args(arg_name: &str, args: &[String]) {
|
||||
for arg in args {
|
||||
if arg.as_str() == "--" {
|
||||
error_msg!("`{}` cannot have `--` as argument", arg_name);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check cfg that is in the form that we can accept
|
||||
fn check_cfg(cfg: &str) {
|
||||
if FEATURE_REGEX.captures(cfg).is_none() {
|
||||
error_msg!("{} is not allowed to used after `qemu` in `OSDK.toml`. Currently we only allowed cfg like `cfg(feature=\"foo\")`", cfg);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref FEATURE_REGEX: Regex = Regex::new(r#"cfg\(feature="(?P<feature>\w+)"\)"#).unwrap();
|
||||
}
|
357
osdk/src/config_manager/mod.rs
Normal file
357
osdk/src/config_manager/mod.rs
Normal file
@ -0,0 +1,357 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! This module is responsible for parsing configuration files and combining them with command-line parameters
|
||||
//! to obtain the final configuration, it will also try searching system to fill valid values for specific
|
||||
//! arguments if the arguments is missing, e.g., the path of QEMU. The final configuration is stored in `BuildConfig`,
|
||||
//! `RunConfig` and `TestConfig`. These `*Config` are used for `build`, `run` and `test` subcommand.
|
||||
|
||||
pub mod boot;
|
||||
pub mod manifest;
|
||||
pub mod qemu;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, process};
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use which::which;
|
||||
|
||||
use self::boot::BootLoader;
|
||||
use self::manifest::{OsdkManifest, TomlManifest};
|
||||
use crate::cli::{BuildArgs, CargoArgs, OsdkArgs, RunArgs, TestArgs};
|
||||
use crate::error::Errno;
|
||||
use crate::utils::get_cargo_metadata;
|
||||
use crate::{error_msg, warn_msg};
|
||||
|
||||
/// Configurations for build subcommand
|
||||
#[derive(Debug)]
|
||||
pub struct BuildConfig {
|
||||
pub manifest: OsdkManifest,
|
||||
pub cargo_args: CargoArgs,
|
||||
}
|
||||
|
||||
impl BuildConfig {
|
||||
pub fn parse(args: &BuildArgs) -> Self {
|
||||
let cargo_args = split_features(&args.cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args);
|
||||
apply_cli_args(&mut manifest, &args.osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
Self {
|
||||
manifest,
|
||||
cargo_args,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configurations for run subcommand
|
||||
#[derive(Debug)]
|
||||
pub struct RunConfig {
|
||||
pub manifest: OsdkManifest,
|
||||
pub cargo_args: CargoArgs,
|
||||
}
|
||||
|
||||
impl RunConfig {
|
||||
pub fn parse(args: &RunArgs) -> Self {
|
||||
let cargo_args = split_features(&args.cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args);
|
||||
apply_cli_args(&mut manifest, &args.osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
Self {
|
||||
manifest,
|
||||
cargo_args,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configurations for test subcommand
|
||||
#[derive(Debug)]
|
||||
pub struct TestConfig {
|
||||
pub manifest: OsdkManifest,
|
||||
pub cargo_args: CargoArgs,
|
||||
pub test_name: Option<String>,
|
||||
}
|
||||
|
||||
impl TestConfig {
|
||||
pub fn parse(args: &TestArgs) -> Self {
|
||||
let cargo_args = split_features(&args.cargo_args);
|
||||
let mut manifest = load_osdk_manifest(&cargo_args);
|
||||
apply_cli_args(&mut manifest, &args.osdk_args);
|
||||
try_fill_system_configs(&mut manifest);
|
||||
Self {
|
||||
manifest,
|
||||
cargo_args,
|
||||
test_name: args.test_name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_osdk_manifest(cargo_args: &CargoArgs) -> OsdkManifest {
|
||||
let manifest_path = {
|
||||
let feature_strings = get_feature_strings(cargo_args);
|
||||
let cargo_metadata = get_cargo_metadata(None::<&str>, Some(&feature_strings));
|
||||
let workspace_root = cargo_metadata
|
||||
.get("workspace_root")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap();
|
||||
PathBuf::from(workspace_root).join("OSDK.toml")
|
||||
};
|
||||
|
||||
let Ok(contents) = fs::read_to_string(&manifest_path) else {
|
||||
error_msg!(
|
||||
"Cannot read file {}",
|
||||
manifest_path.to_string_lossy().to_string()
|
||||
);
|
||||
process::exit(Errno::GetMetadata as _);
|
||||
};
|
||||
|
||||
let toml_manifest: TomlManifest = toml::from_str(&contents).unwrap();
|
||||
OsdkManifest::from_toml_manifest(toml_manifest, &cargo_args.features)
|
||||
}
|
||||
|
||||
/// Split `features` in `cargo_args` to ensure each string contains exactly one feature.
|
||||
/// This method will spilt features seperated by comma in one string as multiple strings.
|
||||
fn split_features(cargo_args: &CargoArgs) -> CargoArgs {
|
||||
let mut features = Vec::new();
|
||||
|
||||
for feature in cargo_args.features.iter() {
|
||||
for feature in feature.split(',') {
|
||||
if !feature.is_empty() {
|
||||
features.push(feature.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CargoArgs {
|
||||
release: cargo_args.release,
|
||||
features,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_feature_strings(cargo_args: &CargoArgs) -> Vec<String> {
|
||||
cargo_args
|
||||
.features
|
||||
.iter()
|
||||
.map(|feature| format!("--features={}", feature))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn try_fill_system_configs(manifest: &mut OsdkManifest) {
|
||||
if manifest.qemu.path.is_none() {
|
||||
if let Ok(path) = which("qemu-system-x86_64") {
|
||||
trace!("system qemu path: {:?}", path);
|
||||
manifest.qemu.path = Some(path);
|
||||
} else {
|
||||
warn_msg!("Cannot find qemu-system-x86_64 in your system. ")
|
||||
}
|
||||
}
|
||||
|
||||
if manifest.boot.grub_mkrescue.is_none() && manifest.boot.loader == BootLoader::Grub {
|
||||
if let Ok(path) = which("grub-mkrescue") {
|
||||
trace!("system grub-mkrescue path: {:?}", path);
|
||||
manifest.boot.grub_mkrescue = Some(path);
|
||||
} else {
|
||||
warn_msg!("Cannot find grub-mkrescue in your system.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_cli_args(manifest: &mut OsdkManifest, args: &OsdkArgs) {
|
||||
let mut init_args = split_kcmd_args(&mut manifest.kcmd_args);
|
||||
apply_kv_array(&mut manifest.kcmd_args, &args.kcmd_args, "=", &[]);
|
||||
init_args.append(&mut args.init_args.clone());
|
||||
|
||||
manifest.kcmd_args.push("--".to_string());
|
||||
for init_arg in init_args {
|
||||
for seperated_arg in init_arg.split(' ') {
|
||||
manifest.kcmd_args.push(seperated_arg.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
apply_option(&mut manifest.initramfs, &args.initramfs);
|
||||
apply_option(&mut manifest.boot.ovmf, &args.boot_ovmf);
|
||||
apply_option(&mut manifest.boot.grub_mkrescue, &args.boot_grub_mkrescue);
|
||||
apply_item(&mut manifest.boot.loader, &args.boot_loader);
|
||||
apply_item(&mut manifest.boot.protocol, &args.boot_protocol);
|
||||
apply_option(&mut manifest.qemu.path, &args.qemu_path);
|
||||
apply_item(&mut manifest.qemu.machine, &args.qemu_machine);
|
||||
|
||||
// check qemu_args
|
||||
for arg in manifest.qemu.args.iter() {
|
||||
qemu::check_qemu_arg(arg);
|
||||
}
|
||||
for arg in args.qemu_args.iter() {
|
||||
qemu::check_qemu_arg(arg);
|
||||
}
|
||||
|
||||
apply_kv_array(
|
||||
&mut manifest.qemu.args,
|
||||
&args.qemu_args,
|
||||
" ",
|
||||
qemu::MULTI_VALUE_KEYS,
|
||||
);
|
||||
}
|
||||
|
||||
fn apply_item<'a, T: From<&'a str> + Clone>(item: &mut T, arg: &Option<T>) {
|
||||
let Some(arg) = arg.clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
*item = arg;
|
||||
}
|
||||
|
||||
fn apply_option<'a, T: From<&'a str> + Clone>(item: &mut Option<T>, arg: &Option<T>) {
|
||||
let Some(arg) = arg.clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
*item = Some(arg);
|
||||
}
|
||||
|
||||
pub fn apply_kv_array(
|
||||
array: &mut Vec<String>,
|
||||
args: &Vec<String>,
|
||||
seperator: &str,
|
||||
multi_value_keys: &[&str],
|
||||
) {
|
||||
let multi_value_keys = {
|
||||
let mut inferred_keys = infer_multi_value_keys(array, seperator);
|
||||
for key in multi_value_keys {
|
||||
inferred_keys.insert(key.to_string());
|
||||
}
|
||||
inferred_keys
|
||||
};
|
||||
|
||||
debug!("multi value keys: {:?}", multi_value_keys);
|
||||
|
||||
// We use IndexMap to keep key orders
|
||||
let mut key_strings = IndexMap::new();
|
||||
let mut multi_value_key_strings: IndexMap<String, Vec<String>> = IndexMap::new();
|
||||
for item in array.drain(..) {
|
||||
// Each key-value string has two patterns:
|
||||
// 1. Seperated by separator: key value / key=value
|
||||
if let Some(key) = get_key(&item, seperator) {
|
||||
if multi_value_keys.contains(&key) {
|
||||
if let Some(v) = multi_value_key_strings.get_mut(&key) {
|
||||
v.push(item);
|
||||
} else {
|
||||
let v = vec![item];
|
||||
multi_value_key_strings.insert(key, v);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
key_strings.insert(key, item);
|
||||
continue;
|
||||
}
|
||||
// 2. Only key, no value
|
||||
key_strings.insert(item.clone(), item);
|
||||
}
|
||||
|
||||
for arg in args {
|
||||
if let Some(key) = get_key(arg, seperator) {
|
||||
if multi_value_keys.contains(&key) {
|
||||
if let Some(v) = multi_value_key_strings.get_mut(&key) {
|
||||
v.push(arg.to_owned());
|
||||
} else {
|
||||
let v = vec![arg.to_owned()];
|
||||
multi_value_key_strings.insert(key, v);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
key_strings.insert(key, arg.to_owned());
|
||||
continue;
|
||||
}
|
||||
|
||||
key_strings.insert(arg.to_owned(), arg.to_owned());
|
||||
}
|
||||
|
||||
*array = key_strings.into_iter().map(|(_, value)| value).collect();
|
||||
|
||||
for (_, mut values) in multi_value_key_strings {
|
||||
array.append(&mut values);
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_multi_value_keys(array: &Vec<String>, seperator: &str) -> IndexSet<String> {
|
||||
let mut multi_val_keys = IndexSet::new();
|
||||
|
||||
let mut occured_keys = IndexSet::new();
|
||||
for item in array {
|
||||
let Some(key) = get_key(item, seperator) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if occured_keys.contains(&key) {
|
||||
multi_val_keys.insert(key);
|
||||
} else {
|
||||
occured_keys.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
multi_val_keys
|
||||
}
|
||||
|
||||
pub fn get_key(item: &str, seperator: &str) -> Option<String> {
|
||||
let split = item.split(seperator).collect::<Vec<_>>();
|
||||
let len = split.len();
|
||||
if len > 2 || len == 0 {
|
||||
error_msg!("`{}` is an invalid argument.", item);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
}
|
||||
|
||||
if len == 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let key = split.first().unwrap();
|
||||
|
||||
Some(key.to_string())
|
||||
}
|
||||
|
||||
fn split_kcmd_args(kcmd_args: &mut Vec<String>) -> Vec<String> {
|
||||
let seperator = "--";
|
||||
let index = kcmd_args.iter().position(|item| item.as_str() == seperator);
|
||||
let Some(index) = index else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut init_args = kcmd_args.split_off(index);
|
||||
init_args.remove(0);
|
||||
init_args
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_kcmd_args_test() {
|
||||
let mut kcmd_args = ["init=/bin/sh", "--", "sh", "-l"]
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect();
|
||||
let init_args = split_kcmd_args(&mut kcmd_args);
|
||||
let expected_kcmd_args: Vec<_> = ["init=/bin/sh"].iter().map(ToString::to_string).collect();
|
||||
assert_eq!(kcmd_args, expected_kcmd_args);
|
||||
let expecetd_init_args: Vec<_> = ["sh", "-l"].iter().map(ToString::to_string).collect();
|
||||
assert_eq!(init_args, expecetd_init_args);
|
||||
|
||||
let mut kcmd_args = ["init=/bin/sh", "--"]
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect();
|
||||
let init_args = split_kcmd_args(&mut kcmd_args);
|
||||
let expected_kcmd_args: Vec<_> = ["init=/bin/sh"].iter().map(ToString::to_string).collect();
|
||||
assert_eq!(kcmd_args, expected_kcmd_args);
|
||||
let expecetd_init_args: Vec<String> = Vec::new();
|
||||
assert_eq!(init_args, expecetd_init_args);
|
||||
|
||||
let mut kcmd_args = ["init=/bin/sh", "shell=/bin/sh"]
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect();
|
||||
let init_args = split_kcmd_args(&mut kcmd_args);
|
||||
let expected_kcmd_args: Vec<_> = ["init=/bin/sh", "shell=/bin/sh"]
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect();
|
||||
assert_eq!(kcmd_args, expected_kcmd_args);
|
||||
let expecetd_init_args: Vec<String> = Vec::new();
|
||||
assert_eq!(init_args, expecetd_init_args);
|
||||
}
|
180
osdk/src/config_manager/qemu.rs
Normal file
180
osdk/src/config_manager/qemu.rs
Normal file
@ -0,0 +1,180 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
use std::{fmt, process};
|
||||
|
||||
use serde::de::{self, Visitor};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use crate::error::Errno;
|
||||
use crate::error_msg;
|
||||
|
||||
use super::get_key;
|
||||
|
||||
/// Arguments for creating bootdev image and how to boot with vmm.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Qemu {
|
||||
/// The additional arguments for running qemu, except `-cpu` and `-machine`.
|
||||
#[serde(default)]
|
||||
pub args: Vec<String>,
|
||||
/// The `-machine` argument for running qemu.
|
||||
#[serde(default)]
|
||||
pub machine: QemuMachine,
|
||||
/// The path of qemu.
|
||||
pub path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)]
|
||||
pub struct CfgQemu {
|
||||
pub default: Qemu,
|
||||
pub cfg: Option<BTreeMap<String, Qemu>>,
|
||||
}
|
||||
|
||||
impl CfgQemu {
|
||||
pub fn new(default: Qemu, cfg: Option<BTreeMap<String, Qemu>>) -> Self {
|
||||
Self { default, cfg }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for CfgQemu {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
enum Field {
|
||||
Path,
|
||||
Args,
|
||||
Machine,
|
||||
Cfg(String),
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Field {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct FieldVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for FieldVisitor {
|
||||
type Value = Field;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("`path`, `args`, `machine` or cfg")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
match v {
|
||||
"args" => Ok(Field::Args),
|
||||
"machine" => Ok(Field::Machine),
|
||||
"path" => Ok(Field::Path),
|
||||
v => Ok(Field::Cfg(v.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_identifier(FieldVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
struct CfgQemuVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for CfgQemuVisitor {
|
||||
type Value = CfgQemu;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("struct CfgQemu")
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: de::MapAccess<'de>,
|
||||
{
|
||||
let mut default = Qemu::default();
|
||||
let mut cfgs = BTreeMap::new();
|
||||
|
||||
while let Some(key) = map.next_key()? {
|
||||
match key {
|
||||
Field::Args => {
|
||||
default.args = map.next_value()?;
|
||||
}
|
||||
Field::Machine => {
|
||||
default.machine = map.next_value()?;
|
||||
}
|
||||
Field::Path => {
|
||||
default.path = map.next_value()?;
|
||||
}
|
||||
Field::Cfg(cfg) => {
|
||||
let qemu_args = map.next_value()?;
|
||||
cfgs.insert(cfg, qemu_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CfgQemu::new(default, Some(cfgs)))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_struct("CfgQemu", &["default", "cfg"], CfgQemuVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum QemuMachine {
|
||||
Microvm,
|
||||
#[default]
|
||||
Q35,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for QemuMachine {
|
||||
fn from(value: &'a str) -> Self {
|
||||
match value {
|
||||
"microvm" => Self::Microvm,
|
||||
"q35" => Self::Q35,
|
||||
_ => {
|
||||
error_msg!("{} is not a valid option for `qemu.machine`", value);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Below are keys in qemu arguments. The key list is not complete.
|
||||
|
||||
/// Keys with multiple values
|
||||
pub const MULTI_VALUE_KEYS: &[&str] = &["-device", "-chardev", "-object", "-netdev", "-drive"];
|
||||
/// Keys with only single value
|
||||
pub const SINGLE_VALUE_KEYS: &[&str] = &["-m", "-serial", "-monitor", "-display"];
|
||||
/// Keys with no value
|
||||
pub const NO_VALUE_KEYS: &[&str] = &["--no-reboot", "-nographic", "-enable-kvm"];
|
||||
/// Keys are not allowed to set in configuration files and command line
|
||||
pub const NOT_ALLOWED_TO_SET_KEYS: &[&str] = &["-cpu", "-machine", "-kernel", "-initrd", "-cdrom"];
|
||||
|
||||
pub fn check_qemu_arg(arg: &str) {
|
||||
let key = if let Some(key) = get_key(arg, " ") {
|
||||
key
|
||||
} else {
|
||||
arg.to_string()
|
||||
};
|
||||
|
||||
if NOT_ALLOWED_TO_SET_KEYS.contains(&key.as_str()) {
|
||||
error_msg!("`{}` is not allowed to set", arg);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
}
|
||||
|
||||
if NO_VALUE_KEYS.contains(&key.as_str()) && key.as_str() != arg {
|
||||
error_msg!("`{}` cannot have value", arg);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
}
|
||||
|
||||
if (SINGLE_VALUE_KEYS.contains(&key.as_str()) || MULTI_VALUE_KEYS.contains(&key.as_str()))
|
||||
&& key.as_str() == arg
|
||||
{
|
||||
error_msg!("`{}` should have value", arg);
|
||||
process::exit(Errno::ParseMetadata as _);
|
||||
}
|
||||
}
|
37
osdk/src/error.rs
Normal file
37
osdk/src/error.rs
Normal file
@ -0,0 +1,37 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Errno {
|
||||
CreateCrate = 1,
|
||||
GetMetadata = 2,
|
||||
AddRustToolchain = 3,
|
||||
ParseMetadata = 4,
|
||||
ExecuteCommand = 5,
|
||||
}
|
||||
|
||||
/// Print error message to console
|
||||
#[macro_export]
|
||||
macro_rules! error_msg {
|
||||
() => {
|
||||
std::eprint!("")
|
||||
};
|
||||
($($arg:tt)*) => {{
|
||||
std::eprint!("[Error]: ");
|
||||
std::eprint!($($arg)*);
|
||||
std::eprint!("\n")
|
||||
}};
|
||||
}
|
||||
|
||||
/// Print warning message to console
|
||||
#[macro_export]
|
||||
macro_rules! warn_msg {
|
||||
() => {
|
||||
std::eprint!("")
|
||||
};
|
||||
($($arg:tt)*) => {{
|
||||
std::eprint!("[Warn]: ");
|
||||
std::eprint!($($arg)*);
|
||||
std::eprint!("\n")
|
||||
}};
|
||||
}
|
24
osdk/src/main.rs
Normal file
24
osdk/src/main.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use env_logger::Env;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
mod cli;
|
||||
mod commands;
|
||||
mod config_manager;
|
||||
mod error;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod utils;
|
||||
|
||||
fn main() {
|
||||
// init logger
|
||||
let env = Env::new().filter("OSDK_LOG_LEVEL");
|
||||
env_logger::init_from_env(env);
|
||||
|
||||
cli::main();
|
||||
}
|
52
osdk/src/test/cli.rs
Normal file
52
osdk/src/test/cli.rs
Normal file
@ -0,0 +1,52 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::utils::*;
|
||||
|
||||
#[test]
|
||||
fn cli_help_message() {
|
||||
let output = cargo_osdk(&["-h"]).output().unwrap();
|
||||
assert_success(&output);
|
||||
assert_stdout_contains_msg(&output, "cargo osdk <COMMAND>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_new_help_message() {
|
||||
let output = cargo_osdk(&["new", "-h"]).output().unwrap();
|
||||
assert_success(&output);
|
||||
assert_stdout_contains_msg(&output, "cargo osdk new [OPTIONS] <name>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_build_help_message() {
|
||||
let output = cargo_osdk(&["build", "-h"]).output().unwrap();
|
||||
assert_success(&output);
|
||||
assert_stdout_contains_msg(&output, "cargo osdk build [OPTIONS]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_run_help_message() {
|
||||
let output = cargo_osdk(&["run", "-h"]).output().unwrap();
|
||||
assert_success(&output);
|
||||
assert_stdout_contains_msg(&output, "cargo osdk run [OPTIONS]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_test_help_message() {
|
||||
let output = cargo_osdk(&["test", "-h"]).output().unwrap();
|
||||
assert_success(&output);
|
||||
assert_stdout_contains_msg(&output, "cargo osdk test [OPTIONS] [TESTNAME]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_check_help_message() {
|
||||
let output = cargo_osdk(&["check", "-h"]).output().unwrap();
|
||||
assert_success(&output);
|
||||
assert_stdout_contains_msg(&output, "cargo osdk check");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cli_clippy_help_message() {
|
||||
let output = cargo_osdk(&["clippy", "-h"]).output().unwrap();
|
||||
assert_success(&output);
|
||||
assert_stdout_contains_msg(&output, "cargo osdk clippy");
|
||||
}
|
3
osdk/src/test/commands/mod.rs
Normal file
3
osdk/src/test/commands/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod new;
|
50
osdk/src/test/commands/new.rs
Normal file
50
osdk/src/test/commands/new.rs
Normal file
@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::fs::remove_dir_all;
|
||||
|
||||
use crate::test::utils::*;
|
||||
|
||||
const KERNEL_NAME: &str = "myos";
|
||||
const LIB_NAME: &str = "my_module";
|
||||
|
||||
#[test]
|
||||
fn create_kernel_in_workspace() {
|
||||
const WORKSPACE_NAME: &str = "kernel_workspace";
|
||||
create_workspace(WORKSPACE_NAME, &[KERNEL_NAME]);
|
||||
let mut cargo_osdk = cargo_osdk(["new", "--kernel", KERNEL_NAME]);
|
||||
cargo_osdk.current_dir(WORKSPACE_NAME);
|
||||
let output = cargo_osdk.output().unwrap();
|
||||
assert_success(&output);
|
||||
remove_dir_all(WORKSPACE_NAME).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_lib_in_workspace() {
|
||||
const WORKSPACE_NAME: &str = "lib_workspace";
|
||||
create_workspace(WORKSPACE_NAME, &[LIB_NAME]);
|
||||
let mut cargo_osdk = cargo_osdk(["new", LIB_NAME]);
|
||||
cargo_osdk.current_dir(WORKSPACE_NAME);
|
||||
let output = cargo_osdk.output().unwrap();
|
||||
assert_success(&output);
|
||||
remove_dir_all(WORKSPACE_NAME).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_two_crates_in_workspace() {
|
||||
const WORKSPACE_NAME: &str = "my_workspace";
|
||||
create_workspace(WORKSPACE_NAME, &[LIB_NAME]);
|
||||
// Create lib crate
|
||||
let mut command = cargo_osdk(["new", LIB_NAME]);
|
||||
command.current_dir(WORKSPACE_NAME);
|
||||
let output = command.output().unwrap();
|
||||
assert_success(&output);
|
||||
|
||||
add_member_to_workspace(WORKSPACE_NAME, KERNEL_NAME);
|
||||
// Create kernel crate
|
||||
let mut command = cargo_osdk(["new", "--kernel", KERNEL_NAME]);
|
||||
command.current_dir(WORKSPACE_NAME);
|
||||
let output = command.output().unwrap();
|
||||
assert_success(&output);
|
||||
|
||||
remove_dir_all(WORKSPACE_NAME).unwrap();
|
||||
}
|
20
osdk/src/test/config_manager/OSDK.toml.conditional
Normal file
20
osdk/src/test/config_manager/OSDK.toml.conditional
Normal file
@ -0,0 +1,20 @@
|
||||
initramfs="./build/initramfs.cpio.gz"
|
||||
|
||||
[qemu]
|
||||
path = "/usr/bin/qemu-system-x86_64"
|
||||
args = ["-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
|
||||
"-device virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off",
|
||||
"-device virtio-serial-pci,disable-legacy=on,disable-modern=off"
|
||||
]
|
||||
|
||||
[qemu.'cfg(feature="intel_tdx")']
|
||||
path = "/usr/local/sbin/qemu-kvm"
|
||||
|
||||
[qemu.'cfg(feature="iommu")']
|
||||
path = "/usr/bin/qemu-system-x86_64"
|
||||
args = [
|
||||
"-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on",
|
||||
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off,iommu_platform=on,ats=on",
|
||||
"-device intel-iommu,intremap=on,device-iotlb=on",
|
||||
"-device ioh3420,id=pcie.0,chassis=1",
|
||||
]
|
0
osdk/src/test/config_manager/OSDK.toml.empty
Normal file
0
osdk/src/test/config_manager/OSDK.toml.empty
Normal file
22
osdk/src/test/config_manager/OSDK.toml.full
Normal file
22
osdk/src/test/config_manager/OSDK.toml.full
Normal file
@ -0,0 +1,22 @@
|
||||
kcmd_args = ["init=/bin/busybox", "path=/usr/local/bin"]
|
||||
init_args = ["sh", "-l"]
|
||||
initramfs="./build/initramfs.cpio.gz"
|
||||
|
||||
[boot]
|
||||
loader = "grub"
|
||||
protocol = "multiboot2"
|
||||
grub-mkrescue = "/usr/bin/grub-mkrescue"
|
||||
ovmf = "/usr/bin/ovmf"
|
||||
|
||||
[qemu]
|
||||
path = "/usr/bin/qemu-system-x86_64"
|
||||
machine = "q35"
|
||||
args = [
|
||||
"-enable-kvm",
|
||||
"-m 2G",
|
||||
"-s /tmp/aster.sock",
|
||||
"--no-reboot",
|
||||
"-nographic",
|
||||
"-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off",
|
||||
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
|
||||
]
|
163
osdk/src/test/config_manager/manifest.rs
Normal file
163
osdk/src/test/config_manager/manifest.rs
Normal file
@ -0,0 +1,163 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::config_manager::manifest::{OsdkManifest, TomlManifest, FEATURE_REGEX};
|
||||
|
||||
use crate::cli::CargoArgs;
|
||||
use crate::config_manager::get_feature_strings;
|
||||
use crate::test::utils::{assert_success, cargo_osdk, create_workspace};
|
||||
|
||||
#[test]
|
||||
fn deserialize_osdk_manifest() {
|
||||
let content = include_str!("OSDK.toml.empty");
|
||||
let osdk_manifest: TomlManifest = toml::from_str(content).unwrap();
|
||||
assert!(osdk_manifest == TomlManifest::default());
|
||||
|
||||
let content = include_str!("OSDK.toml.full");
|
||||
let osdk_manifest: TomlManifest = toml::from_str(content).unwrap();
|
||||
assert!(osdk_manifest.boot.grub_mkrescue.unwrap() == PathBuf::from("/usr/bin/grub-mkrescue"));
|
||||
assert!(
|
||||
osdk_manifest.qemu.default.path.unwrap() == PathBuf::from("/usr/bin/qemu-system-x86_64")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_osdk_manifest() {
|
||||
let manifest = TomlManifest::default();
|
||||
let contents = toml::to_string(&manifest).unwrap();
|
||||
fs::write("OSDK.toml", contents).unwrap();
|
||||
fs::remove_file("OSDK.toml").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_conditional_osdk_manifest() {
|
||||
let content = include_str!("OSDK.toml.conditional");
|
||||
let manifest: TomlManifest = toml::from_str(content).unwrap();
|
||||
println!("manifest = {:?}", manifest);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_manifest() {
|
||||
let workspace = "workspace_foo";
|
||||
let kernel_name: &str = "foo_os";
|
||||
create_workspace(workspace, &[kernel_name]);
|
||||
create_osdk_kernel(kernel_name, workspace);
|
||||
let cargo_args = CargoArgs::default();
|
||||
cargo_osdk_build(PathBuf::from(workspace).join(kernel_name), &cargo_args);
|
||||
fs::remove_dir_all(workspace).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_manifest_conditional() {
|
||||
let workspace = "workspace_bar";
|
||||
let kernel_name: &str = "bar_os";
|
||||
create_workspace(workspace, &[kernel_name]);
|
||||
create_osdk_kernel_with_features(kernel_name, &["intel_tdx", "iommu"], workspace);
|
||||
let contents = include_str!("OSDK.toml.conditional");
|
||||
let path = PathBuf::from(workspace).join("OSDK.toml");
|
||||
fs::write(path, contents).unwrap();
|
||||
|
||||
let cargo_args = CargoArgs {
|
||||
release: true,
|
||||
features: vec![String::from("iommu")],
|
||||
};
|
||||
cargo_osdk_build(PathBuf::from(workspace).join(kernel_name), &cargo_args);
|
||||
|
||||
fs::remove_dir_all(workspace).unwrap();
|
||||
}
|
||||
|
||||
fn create_osdk_kernel(name: &str, current_dir: &str) {
|
||||
let output = cargo_osdk(&["new", "--kernel", name])
|
||||
.current_dir(current_dir)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert_success(&output);
|
||||
}
|
||||
|
||||
fn create_osdk_kernel_with_features(name: &str, features: &[&str], current_dir: &str) {
|
||||
create_osdk_kernel(name, current_dir);
|
||||
let manifest_path = PathBuf::from(current_dir).join(name).join("Cargo.toml");
|
||||
let contents = fs::read_to_string(&manifest_path).unwrap();
|
||||
let mut manifest: toml::Table = toml::from_str(&contents).unwrap();
|
||||
|
||||
let mut features_table = toml::Table::new();
|
||||
for feature in features {
|
||||
features_table.insert(feature.to_string(), toml::Value::Array(Vec::new()));
|
||||
}
|
||||
manifest.insert("features".to_string(), toml::Value::Table(features_table));
|
||||
|
||||
fs::write(&manifest_path, manifest.to_string()).unwrap();
|
||||
}
|
||||
|
||||
fn cargo_osdk_build<P: AsRef<Path>>(current_dir: P, cargo_args: &CargoArgs) {
|
||||
let args = get_feature_strings(cargo_args);
|
||||
let mut command = cargo_osdk(&["build"]);
|
||||
command.args(args);
|
||||
command.current_dir(current_dir);
|
||||
let output = command.output().unwrap();
|
||||
assert_success(&output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn conditional_manifest() {
|
||||
let toml_manifest: TomlManifest = {
|
||||
let content = include_str!("OSDK.toml.conditional");
|
||||
toml::from_str(content).unwrap()
|
||||
};
|
||||
|
||||
assert!(toml_manifest.qemu.cfg.is_some());
|
||||
assert!(toml_manifest
|
||||
.qemu
|
||||
.cfg
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains_key(&String::from("cfg(feature=\"intel_tdx\")")));
|
||||
assert!(toml_manifest
|
||||
.qemu
|
||||
.cfg
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains_key(&String::from("cfg(feature=\"iommu\")")));
|
||||
|
||||
// No features
|
||||
let features: &[&str] = &[];
|
||||
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), features);
|
||||
assert_eq!(
|
||||
manifest.qemu.path,
|
||||
Some(PathBuf::from("/usr/bin/qemu-system-x86_64"))
|
||||
);
|
||||
assert!(manifest.qemu.args.contains(&String::from(
|
||||
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off"
|
||||
)));
|
||||
|
||||
// Iommu features
|
||||
let features: &[&str] = &["iommu"];
|
||||
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), features);
|
||||
assert_eq!(
|
||||
manifest.qemu.path,
|
||||
Some(PathBuf::from("/usr/bin/qemu-system-x86_64"))
|
||||
);
|
||||
assert!(manifest
|
||||
.qemu
|
||||
.args
|
||||
.contains(&String::from("-device ioh3420,id=pcie.0,chassis=1")));
|
||||
|
||||
// Tdx features
|
||||
let features: &[&str] = &["intel_tdx"];
|
||||
let manifest = OsdkManifest::from_toml_manifest(toml_manifest.clone(), features);
|
||||
assert_eq!(
|
||||
manifest.qemu.path,
|
||||
Some(PathBuf::from("/usr/local/sbin/qemu-kvm"))
|
||||
);
|
||||
assert!(manifest.qemu.args.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_feature() {
|
||||
let text = "cfg(feature=\"abc123_\")";
|
||||
let captures = FEATURE_REGEX.captures(text).unwrap();
|
||||
let feature = captures.name("feature").unwrap().as_str();
|
||||
assert_eq!(feature, "abc123_");
|
||||
}
|
49
osdk/src/test/config_manager/mod.rs
Normal file
49
osdk/src/test/config_manager/mod.rs
Normal file
@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::config_manager::{apply_kv_array, get_key};
|
||||
mod manifest;
|
||||
|
||||
#[test]
|
||||
fn get_key_test() {
|
||||
let string1 = "init=/bin/init";
|
||||
let key = get_key(string1, "=").unwrap();
|
||||
assert_eq!(key.as_str(), "init");
|
||||
|
||||
let string2 = "-m 2G";
|
||||
let key = get_key(string2, " ").unwrap();
|
||||
assert_eq!(key.as_str(), "-m");
|
||||
|
||||
let string3 = "-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off";
|
||||
let key = get_key(string3, " ").unwrap();
|
||||
assert_eq!(key.as_str(), "-device");
|
||||
|
||||
let string4 = "-device";
|
||||
assert!(get_key(string4, " ").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_kv_array_test() {
|
||||
let qemu_args = &[
|
||||
"-enable-kvm",
|
||||
"-m 2G",
|
||||
"-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off",
|
||||
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
|
||||
];
|
||||
|
||||
let args = &["-m 100G", "-device ioh3420,id=pcie.0,chassis=1"];
|
||||
|
||||
let expected = &[
|
||||
"-enable-kvm",
|
||||
"-m 100G",
|
||||
"-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,disable-legacy=on,disable-modern=off",
|
||||
"-device virtio-keyboard-pci,disable-legacy=on,disable-modern=off",
|
||||
"-device ioh3420,id=pcie.0,chassis=1",
|
||||
];
|
||||
|
||||
let mut array = qemu_args.iter().map(ToString::to_string).collect();
|
||||
let args = args.iter().map(ToString::to_string).collect();
|
||||
apply_kv_array(&mut array, &args, " ", &["-device"]);
|
||||
|
||||
let expected: Vec<_> = expected.iter().map(ToString::to_string).collect();
|
||||
assert_eq!(expected, array);
|
||||
}
|
8
osdk/src/test/mod.rs
Normal file
8
osdk/src/test/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! The crate unit test module
|
||||
|
||||
mod cli;
|
||||
mod commands;
|
||||
mod config_manager;
|
||||
mod utils;
|
72
osdk/src/test/utils.rs
Normal file
72
osdk/src/test/utils.rs
Normal file
@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! The common utils for crate unit test
|
||||
|
||||
use assert_cmd::Command;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self, create_dir_all};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Output;
|
||||
|
||||
pub fn cargo_osdk<T: AsRef<OsStr>, I: IntoIterator<Item = T>>(args: I) -> Command {
|
||||
let mut command = Command::cargo_bin("cargo-osdk").unwrap();
|
||||
command.arg("osdk");
|
||||
command.args(args);
|
||||
command
|
||||
}
|
||||
|
||||
pub fn assert_success(output: &Output) {
|
||||
assert!(output.status.success());
|
||||
}
|
||||
|
||||
pub fn assert_stdout_contains_msg(output: &Output, msg: &str) {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(stdout.contains(msg))
|
||||
}
|
||||
|
||||
pub fn create_workspace(workspace_name: &str, members: &[&str]) {
|
||||
let mut table = toml::Table::new();
|
||||
let workspace_table = {
|
||||
let mut table = toml::Table::new();
|
||||
table.insert("resolver".to_string(), toml::Value::String("2".to_string()));
|
||||
|
||||
let members = members
|
||||
.iter()
|
||||
.map(|member| toml::Value::String(member.to_string()))
|
||||
.collect();
|
||||
table.insert("members".to_string(), toml::Value::Array(members));
|
||||
table
|
||||
};
|
||||
|
||||
table.insert("workspace".to_string(), toml::Value::Table(workspace_table));
|
||||
|
||||
create_dir_all(workspace_name).unwrap();
|
||||
let manefest_path = PathBuf::from(workspace_name).join("Cargo.toml");
|
||||
|
||||
let content = table.to_string();
|
||||
fs::write(manefest_path, content).unwrap();
|
||||
|
||||
let osdk_manifest_path = PathBuf::from(workspace_name).join("OSDK.toml");
|
||||
fs::write(osdk_manifest_path, "").unwrap();
|
||||
}
|
||||
|
||||
pub fn add_member_to_workspace(workspace: impl AsRef<Path>, new_member: &str) {
|
||||
let path = PathBuf::from(workspace.as_ref()).join("Cargo.toml");
|
||||
|
||||
let mut workspace_manifest: toml::Table = {
|
||||
let content = fs::read_to_string(&path).unwrap();
|
||||
toml::from_str(&content).unwrap()
|
||||
};
|
||||
|
||||
let members = workspace_manifest
|
||||
.get_mut("workspace")
|
||||
.unwrap()
|
||||
.get_mut("members")
|
||||
.unwrap();
|
||||
if let toml::Value::Array(members) = members {
|
||||
members.push(toml::Value::String(new_member.to_string()));
|
||||
}
|
||||
|
||||
let new_content = workspace_manifest.to_string();
|
||||
fs::write(&path, new_content).unwrap();
|
||||
}
|
57
osdk/src/utils.rs
Normal file
57
osdk/src/utils.rs
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use crate::error::Errno;
|
||||
use crate::error_msg;
|
||||
|
||||
// FIXME: Crates belonging to Asterinas require a different dependency format. The dependency
|
||||
// should be specified using a relative path instead of a URL.
|
||||
pub const ASTER_FRAME_DEP: &str =
|
||||
"aster-frame = { git = \"https://github.com/asterinas/asterinas\", rev = \"f2f991b\" }";
|
||||
|
||||
fn cargo() -> Command {
|
||||
Command::new("cargo")
|
||||
}
|
||||
|
||||
/// Create a new library crate with cargo
|
||||
pub fn cargo_new_lib(crate_name: &str) {
|
||||
let mut command = cargo();
|
||||
command.args(["new", "--lib", crate_name]);
|
||||
let status = command.status().unwrap();
|
||||
if !status.success() {
|
||||
error_msg!("Failed to create new crate");
|
||||
std::process::exit(Errno::CreateCrate as _);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cargo_metadata<S1: AsRef<Path>, S2: AsRef<OsStr>>(
|
||||
current_dir: Option<S1>,
|
||||
cargo_args: Option<&[S2]>,
|
||||
) -> serde_json::Value {
|
||||
let mut command = cargo();
|
||||
command.args(["metadata", "--no-deps", "--format-version", "1"]);
|
||||
|
||||
if let Some(current_dir) = current_dir {
|
||||
command.current_dir(current_dir);
|
||||
}
|
||||
|
||||
if let Some(cargo_args) = cargo_args {
|
||||
command.args(cargo_args);
|
||||
}
|
||||
|
||||
let output = command.output().unwrap();
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
eprintln!("{}", &stderr);
|
||||
|
||||
error_msg!("Failed to get metadata for newly created crate");
|
||||
std::process::exit(Errno::GetMetadata as _);
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
serde_json::from_str(&stdout).unwrap()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user