~ren/polymorphos

aaca99e35e19d22f8b1bb441fd91342c70a37c74 — Lauren Jenkinson 6 months ago 3394eaf main
Remove tasks, kapi/kapi-proc, and builder task support

None of it worked. It was entirely broken. There's no point keeping it
in the repo, especially given we're going to move to interpreted drivers
again.
34 files changed, 24 insertions(+), 1821 deletions(-)

M Cargo.toml
D pmtask-test/Cargo.toml
D pmtask-test/src/lib.rs
D pmtask-vga-textmode/Cargo.toml
D pmtask-vga-textmode/src/buffer.rs
D pmtask-vga-textmode/src/color.rs
D pmtask-vga-textmode/src/lib.rs
M polymorph-builder/src/config/mod.rs
D polymorph-builder/src/config/task.rs
M polymorph-builder/src/lib.rs
M polymorph-builder/src/platform/builder.rs
M polymorph-builder/src/platform/mod.rs
M polymorph-builder/src/scan.rs
D polymorph-builder/src/task/builder.rs
D polymorph-builder/src/task/mod.rs
R polymorph-builder/test-data/{task-and-platform-workspace/Cargo.toml => multiple-platform-workspace/Cargo.toml}
R polymorph-builder/test-data/{task-and-platform-workspace/platform-one/Cargo.toml => multiple-platform-workspace/platform-one/Cargo.toml}
R polymorph-builder/test-data/{task-and-platform-workspace/platform-one/x86_64-none.json => multiple-platform-workspace/platform-one/x86_64-none.json}
A polymorph-builder/test-data/multiple-platform-workspace/platform-two/Cargo.toml
A polymorph-builder/test-data/multiple-platform-workspace/platform-two/x86_64-none.json
D polymorph-builder/test-data/multiple-task-workspace/Cargo.toml
D polymorph-builder/test-data/multiple-task-workspace/task-one/Cargo.toml
D polymorph-builder/test-data/multiple-task-workspace/task-two/Cargo.toml
D polymorph-builder/test-data/single-task/Cargo.toml
D polymorph-builder/test-data/task-and-platform-workspace/task-one/Cargo.toml
D polymorph-builder/test-data/task-and-platform-workspace/task-two/Cargo.toml
D polymorph-kapi-proc/Cargo.toml
D polymorph-kapi-proc/src/lib.rs
D polymorph-kapi/Cargo.toml
D polymorph-kapi/README.md
D polymorph-kapi/src/lib.rs
D polymorph-kapi/src/panic.rs
D polymorph-kapi/src/prelude.rs
D polymorph-kapi/src/task.rs
M Cargo.toml => Cargo.toml +0 -4
@@ 16,11 16,7 @@ cty = "0.2"
members = [
    "polymorph-builder",
    "polymorph-helpers",
    "polymorph-kapi",
    "polymorph-kapi-proc",
    "polymorph-x86-common",
    "polymorph-i686",
    "polymorph-amd64",
    "pmtask-test",
    "pmtask-vga-textmode"
]

D pmtask-test/Cargo.toml => pmtask-test/Cargo.toml +0 -20
@@ 1,20 0,0 @@
[package]
name = "pmtask-test"
version = "0.1.0-dev.1"
authors = ["polymorphOS contributors"]
edition = "2018"
publish = false

[lib]
crate-type = ["staticlib"]

[dependencies]
polymorph-kapi = { path = "../polymorph-kapi" }
polymorph-kapi-proc = { path = "../polymorph-kapi-proc" }
polymorph-helpers = { path = "../polymorph-helpers" }

[package.metadata.polymorph-task]
task-name = "pmtask_test"
supported-architectures = ["*"]
task-depends = []
build-artifact-path = ["{target}", "{buildtype}", "libpmtask_test.a"]

D pmtask-test/src/lib.rs => pmtask-test/src/lib.rs +0 -46
@@ 1,46 0,0 @@
#![no_std]
#![feature(alloc_prelude)]
#![allow(unused_imports, dead_code)]

#[cfg(not(test))]
extern crate core;
#[cfg(test)]
extern crate std as core;
#[macro_use]
extern crate alloc;

use alloc::prelude::v1::*;
use polymorph_kapi::prelude::*;
use polymorph_kapi_proc::pmos_task;

/// Data structure for this task
pub struct TaskTestData {
    act_call_count: usize,
    test_str: Option<String>,
}

impl Default for TaskTestData {
    fn default() -> TaskTestData {
        TaskTestData {
            act_call_count: 0,
            test_str: None,
        }
    }
}

/// Initialize task
///
/// Sets `test_str` in the task's data structure ([`TaskTestData`]) and sets
/// an action function to increment the `act_call_count` every time the task
/// action is called.
pub fn task_initialize(task: &mut PolymorphOSTask<TaskTestData>) {
    task.with_task_data(|d| {
        d.test_str = Some(String::from("Hello world!"));
    });

    task.set_act(|d| {
        d.act_call_count = d.act_call_count.wrapping_add(1);
    });
}

pmos_task!(pmtask_test, TaskTestData, task_initialize);

D pmtask-vga-textmode/Cargo.toml => pmtask-vga-textmode/Cargo.toml +0 -22
@@ 1,22 0,0 @@
[package]
name = "pmtask-vga-textmode"
version = "0.1.0-dev.1"
authors = ["polymorphOS contributors"]
edition = "2018"
publish = false

[lib]
crate-type = ["staticlib"]

[dependencies]
polymorph-kapi = { path = "../polymorph-kapi" }
polymorph-kapi-proc = { path = "../polymorph-kapi-proc" }
polymorph-helpers = { path = "../polymorph-helpers" }
volatile = "0.2.6"
array-init = "0.1.1"

[package.metadata.polymorph-task]
task-name = "pmtask_vga_textmode"
supported-architectures = ["i686", "amd64"]
task-depends = []
build-artifact-path = ["{target}", "{buildtype}", "libpmtask_vga_textmode.a"]

D pmtask-vga-textmode/src/buffer.rs => pmtask-vga-textmode/src/buffer.rs +0 -46
@@ 1,46 0,0 @@
use volatile::Volatile;

use crate::color::ColorCode;
use crate::{BUFFER_HEIGHT, BUFFER_WIDTH};

/// A character for display on the VGA text mode console
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ScreenChar {
    pub ascii_char: u8,
    pub color_code: ColorCode,
}

/// The VGA text mode buffer
pub struct Buffer {
    pub chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}

impl Buffer {
    #[cfg(not(test))]
    pub fn new() -> &'static mut Buffer {
        unsafe { &mut *(0xB8000 as *mut Buffer) }
    }

    #[cfg(test)]
    pub fn new() -> &'static mut Buffer {
        use array_init::array_init;
        use core::default::Default;

        let chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT] = array_init(|_| {
            array_init(|_| {
                Volatile::new(ScreenChar {
                    ascii_char: b' ',
                    color_code: Default::default(),
                })
            })
        });

        let mut b = Buffer { chars };

        unsafe {
            use core::mem;
            mem::transmute(&mut b)
        }
    }
}

D pmtask-vga-textmode/src/color.rs => pmtask-vga-textmode/src/color.rs +0 -36
@@ 1,36 0,0 @@
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Color {
    Black = 0x0,
    Blue = 0x1,
    Green = 0x2,
    Cyan = 0x3,
    Red = 0x4,
    Magenta = 0x5,
    Brown = 0x6,
    Gray = 0x7,
    DarkGray = 0x8,
    BrightBlue = 0x9,
    BrightGreen = 0xA,
    BrightCyan = 0xB,
    BrightRed = 0xC,
    BrightMagenta = 0xD,
    Yellow = 0xE,
    White = 0xF,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ColorCode(u8);

impl ColorCode {
    pub fn new(fg: Color, bg: Color) -> ColorCode {
        ColorCode(((bg as u8) << 4) | (fg as u8))
    }
}

impl Default for ColorCode {
    fn default() -> ColorCode {
        ColorCode::new(Color::Gray, Color::Black)
    }
}

D pmtask-vga-textmode/src/lib.rs => pmtask-vga-textmode/src/lib.rs +0 -167
@@ 1,167 0,0 @@
#![no_std]
#![feature(alloc_prelude)]
#![allow(unused_imports, dead_code)]

#[cfg(not(test))]
extern crate core;
#[cfg(test)]
extern crate std as core;
#[macro_use]
extern crate alloc;

use alloc::prelude::v1::*;
use core::fmt::{self, Write};
use polymorph_helpers::byteclone;
use polymorph_kapi::prelude::*;
use polymorph_kapi_proc::pmos_task;

pub const BUFFER_WIDTH: usize = 80;
pub const BUFFER_HEIGHT: usize = 25;
pub const SUBJECT_CONSOLE_WRITE: u8 = 1;
pub const SUBJECT_CONSOLE_CLEAR: u8 = 2;

pub mod buffer;
pub mod color;
use self::buffer::{Buffer, ScreenChar};
use self::color::ColorCode;

/// VGA console data
pub struct VgaTextModeData {
    buffer: &'static mut Buffer,
    color_code: ColorCode,
    line_position: usize,
}

impl VgaTextModeData {
    /// Creates a new VGA console instance.
    pub fn new() -> VgaTextModeData {
        VgaTextModeData {
            buffer: Buffer::new(),
            color_code: Default::default(),
            line_position: 0,
        }
    }

    /// Writes a single character to the console.
    pub fn write_char(&mut self, c: char) {
        match c {
            '\n' => {
                self.scroll_line();
            }

            '\r' => {
                self.line_position = 0;
            }

            // TODO: handle '\t'
            _ => {
                self.write_char_inner(c);
            }
        }
    }

    fn write_char_inner(&mut self, c: char) {
        let mut bytes = [0u8; 4];

        for byte in c.encode_utf8(&mut bytes).as_bytes() {
            self.buffer.chars[BUFFER_HEIGHT - 1][self.line_position].write(ScreenChar {
                ascii_char: *byte,
                color_code: self.color_code,
            });

            self.line_position += 1;
        }
    }

    /// Scrolls the console text up one line.
    pub fn scroll_line(&mut self) {
        for row in 1..BUFFER_HEIGHT {
            for col in 0..BUFFER_WIDTH {
                let c = self.buffer.chars[row][col].read();
                self.buffer.chars[row - 1][col].write(c);
            }
        }

        for col in 0..BUFFER_WIDTH {
            self.buffer.chars[BUFFER_HEIGHT - 1][col].write(ScreenChar {
                ascii_char: b' ',
                color_code: self.color_code,
            })
        }

        self.line_position = 0;
        // TODO: self.cursor_move();
    }

    /// Clears the screen.
    pub fn clear(&mut self) {
        for row in 0..BUFFER_HEIGHT {
            for col in 0..BUFFER_WIDTH {
                self.buffer.chars[row][col].write(ScreenChar {
                    ascii_char: b' ' as u8,
                    color_code: self.color_code,
                })
            }
        }
    }
}

impl Default for VgaTextModeData {
    fn default() -> VgaTextModeData {
        VgaTextModeData::new()
    }
}

impl Write for VgaTextModeData {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        for c in s.chars() {
            self.write_char(c);
        }

        Ok(())
    }
}

/// Handle task messages
pub fn task_msg(
    data: &mut VgaTextModeData,
    msg_topic: String,
    msg_subject: Vec<u8>,
    msg_data: Vec<u8>,
) {
    match msg_topic.as_str() {
        "console" => {
            if msg_subject.len() >= 1 {
                match msg_subject[0] {
                    SUBJECT_CONSOLE_WRITE => {
                        let data_str = {
                            let sl = msg_data.as_slice();
                            byteclone::bc_string(sl.as_ptr(), sl.len())
                        };

                        let _ = write!(data, "{}", data_str);
                    }

                    SUBJECT_CONSOLE_CLEAR => {
                        data.clear();
                    }

                    _ => {}
                }
            }
        }

        _ => {}
    }
}

/// Initialize task
pub fn task_initialize(task: &mut PolymorphOSTask<VgaTextModeData>) {
    task.with_task_data(|d| {
        d.scroll_line();
    });

    task.set_msg(task_msg);
}

pmos_task!(pmtask_vga_textmode, VgaTextModeData, task_initialize);

M polymorph-builder/src/config/mod.rs => polymorph-builder/src/config/mod.rs +0 -2
@@ 2,8 2,6 @@

mod build;
mod platform;
mod task;

pub use self::build::BuildConfig;
pub use self::platform::PlatformConfig;
pub use self::task::TaskConfig;

D polymorph-builder/src/config/task.rs => polymorph-builder/src/config/task.rs +0 -173
@@ 1,173 0,0 @@
use anyhow::{Context, Result};
use std::default::Default;
use std::fs::File;
use std::io::Read;
use std::path::Path;

use toml::Value;

/// Represents the `package.metadata.polymorph-task` configuration table
#[derive(Debug, Clone)]
pub struct TaskConfig {
    /// Task name
    pub task_name: String,

    /// Architectures supported by this task.
    pub supported_architectures: Vec<String>,

    /// List of task names that this task depends on
    pub task_depends: Vec<String>,

    /// Path to the static library produced by `cargo xbuild`, relative to the
    /// Cargo target directory.
    ///
    /// # Substitutions
    ///
    /// If any of the following strings are present in any of the path
    /// segments, they are replaced:
    ///
    /// - `{target}` - replaced with the target triple
    /// - `{buildtype}` - replaced with the build type (`debug` or `release`)
    pub build_artifact_path: Vec<String>,
}

impl TaskConfig {
    /// Parses the task config out of the given Cargo manifest
    pub fn read_config(manifest_path: &Path) -> Result<TaskConfig> {
        let config = Self::read_config_inner(manifest_path)
            .with_context(|| anyhow!("Failed to read task configuration"))?;

        Ok(config)
    }

    fn read_config_inner(manifest_path: &Path) -> Result<TaskConfig> {
        let cargo_manifest: Value = {
            let mut content = String::new();
            File::open(manifest_path)
                .with_context(|| {
                    format!("Failed to open Cargo manifest at path {:?}", manifest_path)
                })?
                .read_to_string(&mut content)
                .with_context(|| {
                    format!("Failed to read Cargo manifest at path {:?}", manifest_path)
                })?;

            content.parse::<Value>().with_context(|| {
                format!("Failed to parse Cargo manifest at path {:?}", manifest_path)
            })?
        };

        let data = cargo_manifest
            .get("package")
            .and_then(|t| t.get("metadata"))
            .and_then(|t| t.get("polymorph-task"));

        let data = match data {
            None => {
                return Err(anyhow!(
                    "\"package.metadata.polymorph-task\" configuration table not present"
                ))
                .with_context(|| {
                    format!("Failed to parse Cargo manifest at path {:?}", manifest_path)
                })?;
            }
            Some(d) => match d.as_table() {
                Some(t) => t,
                None => {
                    Err(anyhow!("Failed to parse configuration: {}", d)).with_context(|| {
                        format!("Failed to parse Cargo manifest at path {:?}", manifest_path)
                    })?
                }
            },
        };

        let mut config: TaskConfig = Default::default();

        for (key, value) in data {
            match (key.as_str(), value.clone()) {
                ("task-name", Value::String(s)) => config.task_name = From::from(s),

                ("supported-architectures", Value::Array(ar)) => {
                    let mut arches = Vec::new();
                    for val in ar {
                        match val {
                            Value::String(s) => arches.push(s),
                            _ => Err(anyhow!("supported-architectures must be a list of strings"))
                                .with_context(|| {
                                    format!(
                                        "Failed to parse Cargo manifest at path {:?}",
                                        manifest_path
                                    )
                                })?,
                        }
                    }

                    config.supported_architectures = arches;
                }

                ("task-depends", Value::Array(ar)) => {
                    let mut depends = Vec::new();
                    for val in ar {
                        match val {
                            Value::String(s) => depends.push(s),
                            _ => Err(anyhow!("supported-architectures must be a list of strings"))
                                .with_context(|| {
                                    format!(
                                        "Failed to parse Cargo manifest at path {:?}",
                                        manifest_path
                                    )
                                })?,
                        }
                    }

                    config.task_depends = depends;
                }

                ("build-artifact-path", Value::Array(ar)) => {
                    let mut path_elements = Vec::new();
                    for val in ar {
                        match val {
                            Value::String(s) => path_elements.push(s),
                            _ => {
                                Err(anyhow!("build-artifact-path must be a list of strings"))
                                    .with_context(|| {
                                        format!(
                                            "Failed to parse Cargo manifest at path {:?}",
                                            manifest_path
                                        )
                                    })?;
                            }
                        }
                    }

                    config.build_artifact_path = path_elements;
                }

                (key, _) => {
                    Err(anyhow!("Unexpected task config key {:?}", key)).with_context(|| {
                        format!("Failed to parse Cargo manifest at path {:?}", manifest_path)
                    })?
                }
            }
        }

        if config.task_name.len() == 0 || config.supported_architectures.len() == 0 {
            Err(anyhow!("Task config requirements not satisfied")).with_context(|| {
                format!("Failed to parse Cargo manifest at path {:?}", manifest_path)
            })?;
        }

        Ok(config)
    }
}

impl Default for TaskConfig {
    fn default() -> TaskConfig {
        TaskConfig {
            task_name: String::new(),
            supported_architectures: Vec::new(),
            task_depends: Vec::new(),
            build_artifact_path: Vec::new(),
        }
    }
}

M polymorph-builder/src/lib.rs => polymorph-builder/src/lib.rs +0 -1
@@ 11,7 11,6 @@ pub mod cli;
pub mod config;
pub mod platform;
pub mod scan;
pub mod task;

use self::cli::*;


M polymorph-builder/src/platform/builder.rs => polymorph-builder/src/platform/builder.rs +2 -204
@@ 1,4 1,4 @@
use crate::{config::BuildConfig, platform::Platform, scan::FromManifest, task::TaskBuilder};
use crate::{config::BuildConfig, platform::Platform, scan::FromManifest};
use anyhow::{anyhow, Context, Result};
use std::{
    fmt::{self, Display},


@@ 15,10 15,6 @@ pub struct PlatformBuilder {
    pub buildcfg: BuildConfig,
    pub platform: Platform,
    pub quiet: bool,
    pub task_build_ok: bool,
    pub task_build_artifacts: Vec<String>,
    pub task_build_artifact_roots: Vec<String>,
    pub linkattr_ok: bool,
}

impl PlatformBuilder {


@@ 33,10 29,6 @@ impl PlatformBuilder {
            buildcfg,
            platform,
            quiet: false,
            task_build_ok: false,
            task_build_artifacts: Vec::new(),
            task_build_artifact_roots: Vec::new(),
            linkattr_ok: false,
        }
    }



@@ 209,188 201,6 @@ impl PlatformBuilder {
        Ok(())
    }

    /// Performs build on the passed `task_builders`
    ///
    /// Returns a tuple, where the first element is the deduplicated list of
    /// include paths for the task static libraries, and the second element
    /// is the list of all of the names of the task static libraries.
    pub fn task_build_all(&mut self, task_builders: &mut Vec<TaskBuilder>) -> Result<()> {
        log::info!("Starting build of {} enabled task(s)", task_builders.len());

        // Run task builders, collecting artifact paths
        let mut artifacts: Vec<PathBuf> = Vec::new();
        for tbuild in task_builders.iter_mut() {
            tbuild.set_quiet(self.quiet);

            let at = tbuild
                .perform_build()
                .with_context(|| format!("{} - Failed to `perform_build`", &tbuild))?;

            artifacts.push(at);
        }

        // Get the parent directory of each artifact path, convert to a String,
        // and then sort and deduplicate the paths
        let artifact_roots = {
            let mut artifact_roots: Vec<String> = Vec::new();
            for artifact in artifacts.iter() {
                let artifact_root = artifact
                    .parent()
                    .with_context(|| format!("Failed getting artifact parent"))?
                    .as_os_str()
                    .to_str()
                    .with_context(|| format!("Failed converting artifact path to str"))?
                    .to_owned();

                artifact_roots.push(artifact_root);
            }

            // Sort and dedup task artifact roots
            artifact_roots.sort();
            artifact_roots.dedup();
            artifact_roots
        };

        // Construct a list of static library names suitable for passing
        // into the link attribute constructor
        let artifact_names = {
            let mut artifact_names: Vec<String> = Vec::new();
            for artifact in artifacts.iter() {
                let mut artifact_name = artifact
                    .file_name()
                    .with_context(|| format!("Failed getting artifact file name"))?
                    .to_str()
                    .with_context(|| format!("Failed converting artifact file name to str"))?
                    .to_owned();

                if artifact_name.starts_with("lib") {
                    artifact_name.replace_range(..3, "");
                }

                if artifact_name.ends_with(".a") {
                    artifact_name.replace_range((artifact_name.len() - 2).., "");
                }

                artifact_names.push(artifact_name);
            }

            artifact_names
        };

        self.task_build_ok = true;
        self.task_build_artifacts = artifact_names;
        self.task_build_artifact_roots = artifact_roots;

        Ok(())
    }

    /// Performs build on all tasks specified in the build configuration for
    /// this `PlatformBuilder`
    ///
    /// This is a small wrapper around `task_build_all` that performs the
    /// construction of `TaskBuilder` instances automatically. The return
    /// value of this function is the same as `task_build_all`.
    pub fn task_build_from_config(&mut self) -> Result<()> {
        if self.task_build_ok {
            return Ok(());
        }

        let start_time = Instant::now();

        // Get `TaskBuilder` instances for all enabled tasks
        let mut task_builders = TaskBuilder::new_from_config(
            self.scan_paths.iter().map(|x| x.as_path()).collect(),
            self.buildcfg.clone(),
            self.platform.clone(),
        )?;

        self.task_build_all(&mut task_builders)?;

        // Build successful!
        let duration = start_time.elapsed();
        log::info!(
            "{} Task builds successful in {:0.2} seconds",
            &self,
            duration.as_secs() as f64 + (duration.subsec_nanos() as f64 * 1e-9)
        );

        Ok(())
    }

    /// Constructs includeable file for linking task artifacts
    ///
    /// Takes a list of task static library artifact names, and constructs a
    /// file suitable for inclusion in the kernel using `include!()` which
    /// contains the link attributes and `extern` blocks for those static
    /// libraries.
    pub fn task_linkattr(&mut self) -> Result<String> {
        let mut linkattr_file = crate::cargo_target_dir()?;
        linkattr_file.push("polymorphos-task-link.rs.in");
        let linkattr_file_str = linkattr_file
            .as_os_str()
            .to_str()
            .with_context(|| format!("Failed converting link attribute include file path to str"))?
            .to_owned();

        if self.linkattr_ok {
            return Ok(linkattr_file_str);
        }

        let mut link_output: Vec<String> = Vec::new();
        let mut fcall_output: Vec<String> = Vec::new();
        for artifact in self.task_build_artifacts.iter() {
            link_output.push(format!(
                concat!(
                    "#[link(name = \"{artifact}\", kind = \"static\")]\n",
                    "extern \"C\" {{\n",
                    "    fn taskinitfn__{artifact}();",
                    "    fn taskhookstubfn_poll__{artifact}() -> bool;\n",
                    "    fn taskhookstubfn_act__{artifact}();\n",
                    "    fn taskhookstubfn_msg__{artifact}(\n",
                    "        topic: *const u8,\n",
                    "        topic_len: usize,\n",
                    "        subject: *const u8,\n",
                    "        subject_len: usize,\n",
                    "        data: *const u8,\n",
                    "        data_len: usize,\n",
                    "    );\n",
                    "}}\n",
                ),
                artifact = artifact
            ));

            fcall_output.push(format!(
                concat!(
                    "crate::kapi::api_task_register(",
                    "b\"{artifact}\", ",
                    "(taskinitfn__{artifact} as *const u8) as usize, ",
                    "(taskhookstubfn_poll__{artifact} as *const u8) as usize, ",
                    "(taskhookstubfn_act__{artifact} as *const u8) as usize, ",
                    "(taskhookstubfn_msg__{artifact} as *const u8) as usize, ",
                    ");",
                ),
                artifact = artifact
            ));
        }

        let output_content: String = {
            let mut output_content: Vec<String> = link_output.clone();
            output_content.push(format!("\n"));
            output_content.push(format!(
                "pub unsafe fn init_tasks() {{\n{fcall}\n}}",
                fcall = fcall_output.join("\n"),
            ));

            output_content.join("\n")
        };

        fs::write(&linkattr_file, output_content)
            .with_context(|| format!("Failed writing link attribute include file"))?;

        self.linkattr_ok = true;
        Ok(linkattr_file_str)
    }

    /// Retrieves the path to the target specification, and the target triple
    /// defined by that specification
    ///


@@ 617,13 427,7 @@ impl PlatformBuilder {
    }

    pub fn rustflags(&self) -> Result<String> {
        let mut rustflags: Vec<String> = self.platform.rustflags(self.buildcfg.release_mode);

        // Add artifact include paths
        for path in self.task_build_artifact_roots.iter() {
            rustflags.push("-L".to_string());
            rustflags.push(path.to_string());
        }
        let rustflags: Vec<String> = self.platform.rustflags(self.buildcfg.release_mode);

        let rustflags = rustflags.join(" ");
        log::debug!("Build RUSTFLAGS: {:?}", rustflags);


@@ 632,10 436,6 @@ impl PlatformBuilder {

    /// Performs the build.
    pub fn perform_build(&mut self) -> Result<()> {
        // Run task builders, gather artifact paths, and construct the link
        // attribute file for inclusion in the kernel
        self.task_build_from_config()?;

        let start_time = Instant::now();

        log::info!(


@@ 653,7 453,6 @@ impl PlatformBuilder {
        cmd.current_dir(&self.platform.directory);
        cmd.env("RUSTFLAGS", self.rustflags()?);
        cmd.env("CARGO_TARGET_DIR", artifact_path_str);
        cmd.env("POLYMORPH_LINKATTR_FILE", self.task_linkattr()?);

        // If we're not running in quiet mode, attach the stdout/stderr of
        // the build command to our process stdout/stderr


@@ 714,7 513,6 @@ impl PlatformBuilder {
        cmd.current_dir(&self.platform.directory);
        cmd.env("RUSTFLAGS", self.rustflags()?);
        cmd.env("CARGO_TARGET_DIR", artifact_path_str);
        cmd.env("POLYMORPH_LINKATTR_FILE", self.task_linkattr()?);

        // If we're not running in quiet mode, attach the stdout/stderr of
        // the build command to our process stdout/stderr

M polymorph-builder/src/platform/mod.rs => polymorph-builder/src/platform/mod.rs +1 -29
@@ 12,7 12,7 @@ use crate::scan::FromManifest;
mod builder;
pub use self::builder::PlatformBuilder;

/// Information about a platform, including data from the task's Cargo manifest.
/// Information about a platform, including data from the platform's Cargo manifest.
#[derive(Clone)]
pub struct Platform {
    pub directory: PathBuf,


@@ 146,20 146,6 @@ mod tests {
    }

    #[test]
    fn platform_parse_cargo_manifest_fails_on_workspace() {
        let manifest_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "multiple-task-workspace",
            "Cargo.toml",
        ]
        .iter()
        .collect();

        assert!(PlatformConfig::read_config(manifest_path.as_path()).is_err());
    }

    #[test]
    fn platform_construct() {
        let manifest_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),


@@ 178,20 164,6 @@ mod tests {
    }

    #[test]
    fn platform_construct_fails_on_workspace() {
        let manifest_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "multiple-task-workspace",
            "Cargo.toml",
        ]
        .iter()
        .collect();

        assert!(Platform::from_manifest(manifest_path.as_path()).is_err());
    }

    #[test]
    fn platform_scan_constructs_single() {
        let manifest_dir: PathBuf = [env!("CARGO_MANIFEST_DIR"), "test-data", "single-platform"]
            .iter()

M polymorph-builder/src/scan.rs => polymorph-builder/src/scan.rs +2 -18
@@ 114,11 114,11 @@ mod tests {
    use super::*;

    #[test]
    fn task_manifests_from_workspace() {
    fn platform_manifests_from_workspace() {
        let workspace_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "multiple-task-workspace",
            "multiple-platform-workspace",
            "Cargo.toml",
        ]
        .iter()


@@ 128,20 128,4 @@ mod tests {
        assert!(manifests.is_ok());
        assert_eq!(manifests.unwrap().len(), 2);
    }

    #[test]
    fn task_and_platform_manifests_from_workspace() {
        let workspace_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "task-and-platform-workspace",
            "Cargo.toml",
        ]
        .iter()
        .collect();

        let manifests = manifests_from_workspace(workspace_path.as_path());
        assert!(manifests.is_ok());
        assert_eq!(manifests.unwrap().len(), 3);
    }
}

D polymorph-builder/src/task/builder.rs => polymorph-builder/src/task/builder.rs +0 -307
@@ 1,307 0,0 @@
use crate::{config::BuildConfig, platform::Platform, scan::FromManifest, task::Task};
use anyhow::{anyhow, Context, Result};
use std::{
    fmt::{self, Display},
    path::{Path, PathBuf},
    process::{self, Command},
    time::Instant,
};

/// Task build helper
pub struct TaskBuilder {
    pub buildcfg: BuildConfig,
    pub task: Task,
    pub platform: Platform,
    pub quiet: bool,
}

impl TaskBuilder {
    /// Create a `TaskBuilder` for the given task
    pub fn new(buildcfg: BuildConfig, platform: Platform, task: Task) -> TaskBuilder {
        TaskBuilder {
            buildcfg,
            task,
            platform,
            quiet: false,
        }
    }

    /// Scan the paths given in `scan_paths` for the tasks specified in the
    /// given build configuration for the given platform.
    ///
    /// Returns a `Vec<TaskBuilder>` if all the tasks (and their dependencies)
    /// specified in the configuration are present, and support the platform
    /// given.
    pub fn new_from_config(
        scan_paths: Vec<&Path>,
        buildcfg: BuildConfig,
        platform: Platform,
    ) -> Result<Vec<TaskBuilder>> {
        let platform_name = buildcfg.platform.clone();

        // Do a directory scan
        let tasks = Task::scan_directories(scan_paths);
        log::trace!("Available tasks: {:?}", tasks);

        // Get enabled tasks. First pass - only those directly specified in config
        let mut enabled_tasks: Vec<Task> = Vec::new();
        for task_name in buildcfg.enabled_tasks.iter() {
            let normalized_name = task_name.clone().replace("-", "_");

            for av_task in tasks.iter() {
                if av_task.config.task_name == normalized_name {
                    log::trace!("Enabling task {:?}", task_name);
                    enabled_tasks.push(av_task.clone());
                }
            }
        }

        // TODO: Second pass - task dependencies

        // Third pass - check supported architectures for tasks
        for task in enabled_tasks.iter() {
            let mut ok = false;
            for platform_name in task.config.supported_architectures.iter() {
                if platform_name == "*" || platform_name == &buildcfg.platform {
                    ok = true;
                }
            }

            if !ok {
                Err(anyhow!(
                    "Task {:?} does not support current platform ({:?})",
                    &task.config.task_name,
                    &platform_name
                ))?;
            }
        }

        // Construct builders
        let mut builders: Vec<TaskBuilder> = Vec::new();
        for task in enabled_tasks.iter() {
            builders.push(TaskBuilder::new(
                buildcfg.clone(),
                platform.clone(),
                task.clone(),
            ));
        }

        Ok(builders)
    }

    /// Set the "quiet" flag for this build
    pub fn set_quiet(&mut self, quiet: bool) {
        self.quiet = quiet;
    }

    /// Returns build type as a string
    ///
    /// Returns `"release"` if the `release-mode` flag is set in this build's
    /// `BuildConfig`, otherwise it returns `"debug"`.
    pub fn build_type(&self) -> String {
        String::from(if self.buildcfg.release_mode {
            "release"
        } else {
            "debug"
        })
    }

    /// Gets the base artifact path (the Cargo target directory) as a PathBuf
    /// and as a String. The output is suitable for passing to Cargo as the
    /// `CARGO_TARGET_DIR` environment variable.
    pub fn artifact_path_base(&self) -> Result<(PathBuf, String)> {
        let artifact_path = crate::cargo_target_dir()
            .with_context(|| format!("Finding artifact path: failed to get target dir"))?;

        let artifact_path_str = artifact_path
            .as_os_str()
            .to_str()
            .with_context(|| format!("Failed to convert artifact path to string"))?
            .to_string();

        Ok((artifact_path, artifact_path_str))
    }

    /// Returns the path to the build artifact as specified by the task
    /// configuration
    ///
    /// This performs replacements on artifact path elements as specified
    /// on the `build-artifact-path` entry in `TaskConfig`.
    pub fn artifact_path(
        &self,
        target_triple: String,
        buildtype: String,
    ) -> Result<(PathBuf, String)> {
        let (mut artifact_path, _) = self
            .artifact_path_base()
            .with_context(|| format!("Finding artifact path: failed to get target dir"))?;

        for itm in self.task.config.build_artifact_path.iter() {
            let itm = itm
                .clone()
                .replace("{target}", &target_triple)
                .replace("{buildtype}", &buildtype);

            artifact_path.push(itm);
        }

        let artifact_path_str = artifact_path
            .as_os_str()
            .to_str()
            .with_context(|| format!("Failed to convert artifact path to string"))?
            .to_string();

        Ok((artifact_path, artifact_path_str))
    }

    /// Asserts that the build artifact for this task exists
    ///
    /// This function returns an `Ok(())` if the build artifact exists, and
    /// an error type if it does not.
    pub fn artifact_assert_exists(&self) -> Result<()> {
        let (_, target_triple) = self.platform.target_spec_triple()?;
        let (artifact_path, _) = self.artifact_path(target_triple, self.build_type())?;

        // Check that the artifact exists
        log::debug!("Expecting artifact: {:?}", artifact_path);
        if !artifact_path.exists() {
            Err(anyhow!("Artifact does not exist: {:?}", &artifact_path))?;
        }

        Ok(())
    }

    /// Retrieves the path to the target specification, and the target triple
    /// defined by that specification
    ///
    /// # Caveats
    ///
    /// The same caveats apply here as for `Platform::target_spec_triple` -
    /// that is, the target specification is not parsed to retrieve the target
    /// triple, it is solely derived from the file name.
    pub fn target_spec_triple(&self) -> Result<(String, String)> {
        let (target_spec, target_triple) =
            self.platform.target_spec_triple().with_context(|| {
                format!(
                    "Failed getting target spec for platform {:?}",
                    &self.platform.config.platform_name
                )
            })?;

        let target_spec = target_spec
            .as_os_str()
            .to_str()
            .with_context(|| format!("Failed converting target spec path to str"))?
            .to_owned();

        Ok((target_spec, target_triple))
    }

    /// Get the build command executable name and arguments
    ///
    /// Returns a tuple, where the first element is the path of the build
    /// executable (which could be either just an executable name, or a full
    /// path), and the second element is a list of arguments to pass to that
    /// executable.
    pub fn build_executable_args(&self) -> Result<(String, Vec<String>)> {
        // Get target spec
        let (target_spec, _) = self.target_spec_triple()?;

        let executable = crate::cargo_executable();
        let args: Vec<String> = ["xbuild", "--target", "{target}"]
            .iter()
            .map(|x| x.to_string())
            .collect();

        let mut edited_args = Vec::new();
        for itm in args.iter() {
            let itm = itm
                .clone()
                .replace("{target}", &target_spec)
                .replace("{cargo}", &crate::cargo_executable());

            edited_args.push(itm);
        }

        Ok((executable, edited_args))
    }

    /// Performs the build.
    pub fn perform_build(&mut self) -> Result<PathBuf> {
        let start_time = Instant::now();

        log::info!(
            "{} starting perform_build (build type: {:?})",
            &self,
            self.build_type(),
        );

        // Get compiler flags
        let rustflags: String = {
            let rustflags: Vec<String> = self.platform.rustflags(self.buildcfg.release_mode);

            let rustflags = rustflags.join(" ");
            log::trace!("Build RUSTFLAGS: {:?}", rustflags);
            rustflags
        };

        let (_, target_triple) = self.target_spec_triple()?;
        let (artifact_path, _) = self.artifact_path_base()?;
        let (executable, args) = self.build_executable_args()?;

        // Construct command
        let mut cmd = Command::new(executable);
        cmd.args(args);
        cmd.current_dir(&self.task.directory);
        cmd.env("RUSTFLAGS", rustflags);
        cmd.env("CARGO_TARGET_DIR", &artifact_path);
        cmd.env("XBUILD_SYSROOT_PATH", &artifact_path.join("task-sysroot"));

        if !self.quiet {
            cmd.stdout(process::Stdio::inherit());
            cmd.stderr(process::Stdio::inherit());
        }

        log::debug!(
            "Launching build command (in directory {dir:?}): {:?}",
            &cmd,
            dir = &self.task.directory,
        );

        // Run and get output
        let output = cmd
            .output()
            .with_context(|| format!("Failed to launch build command"))?;

        if !output.status.success() {
            Err(anyhow!(
                "Build command exited with error, output {:?}",
                output
            ))?;
        }

        // Make sure our build artifact exists
        self.artifact_assert_exists().with_context(|| {
            format!("The build command succeeded, but the build artifact does not exist?")
        })?;

        // Build successful!
        let duration = start_time.elapsed();
        log::info!(
            "{} Build successful in {:0.2} seconds",
            &self,
            duration.as_secs() as f64 + (duration.subsec_nanos() as f64 * 1e-9)
        );

        let (artifact_path, _) = self.artifact_path(target_triple, self.build_type())?;
        Ok(artifact_path)
    }
}

impl Display for TaskBuilder {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("TaskBuilder")
            .field(&self.task.config.task_name)
            .finish()
    }
}

D polymorph-builder/src/task/mod.rs => polymorph-builder/src/task/mod.rs +0 -187
@@ 1,187 0,0 @@
//! Task configuration and build helpers

use anyhow::{Context, Result};
use std::{
    fmt::{self, Debug},
    path::{Path, PathBuf},
};

use crate::config::TaskConfig;
use crate::scan::FromManifest;

mod builder;
pub use self::builder::TaskBuilder;

/// Information about a task, including data from the task's Cargo manifest.
#[derive(Clone)]
pub struct Task {
    pub directory: PathBuf,
    pub manifest_path: PathBuf,
    pub config: TaskConfig,
}

impl FromManifest for Task {
    fn from_manifest(manifest_path: &Path) -> Result<Self> {
        let task_config: TaskConfig =
            TaskConfig::read_config(manifest_path).with_context(|| {
                format!(
                    "Failed to read task config for manifest {:?}",
                    manifest_path
                )
            })?;

        let manifest_path = manifest_path.clone().to_path_buf();
        let mut directory = manifest_path.clone();
        directory.pop();

        let task = Self {
            directory,
            manifest_path,
            config: task_config,
        };

        Ok(task)
    }
}

impl Debug for Task {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Task")
            .field("name", &self.config.task_name)
            .field("directory", &self.directory)
            .finish()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn task_parse_cargo_manifest() {
        let manifest_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "single-task",
            "Cargo.toml",
        ]
        .iter()
        .collect();

        let task_config = TaskConfig::read_config(manifest_path.as_path()).unwrap();
        assert_eq!(task_config.task_name, String::from("single-task"));
        assert_eq!(task_config.supported_architectures.len(), 1);
        assert_eq!(task_config.task_depends.len(), 0);
    }

    #[test]
    fn task_parse_cargo_manifest_fails_on_nonexistent_path() {
        let manifest_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "this-file-does-not-exist.toml",
        ]
        .iter()
        .collect();

        assert!(TaskConfig::read_config(manifest_path.as_path()).is_err());
    }

    #[test]
    fn task_parse_cargo_manifest_fails_on_workspace() {
        let manifest_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "multiple-task-workspace",
            "Cargo.toml",
        ]
        .iter()
        .collect();

        assert!(TaskConfig::read_config(manifest_path.as_path()).is_err());
    }

    #[test]
    fn task_construct() {
        let manifest_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "single-task",
            "Cargo.toml",
        ]
        .iter()
        .collect();

        let task = Task::from_manifest(manifest_path.as_path()).unwrap();
        assert_eq!(task.manifest_path.as_os_str(), manifest_path.as_os_str());
    }

    #[test]
    fn task_construct_fails_on_workspace() {
        let manifest_path: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "multiple-task-workspace",
            "Cargo.toml",
        ]
        .iter()
        .collect();

        assert!(Task::from_manifest(manifest_path.as_path()).is_err());
    }

    #[test]
    fn task_scan_constructs_single() {
        let manifest_dir: PathBuf = [env!("CARGO_MANIFEST_DIR"), "test-data", "single-task"]
            .iter()
            .collect();

        let tasks = Task::scan_directories(vec![manifest_dir.as_path()]);
        assert_eq!(tasks.len(), 1);
        assert_eq!(
            tasks.first().unwrap().config.task_name,
            String::from("single-task")
        );
    }

    #[test]
    fn task_scan_constructs_multiple() {
        let manifest_dir: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "multiple-task-workspace",
        ]
        .iter()
        .collect();

        let tasks = Task::scan_directories(vec![manifest_dir.as_path()]);
        assert_eq!(tasks.len(), 2);

        let names: Vec<&String> = tasks.iter().map(|t| &t.config.task_name).collect();
        assert!(names.contains(&&String::from("task-one")));
        assert!(names.contains(&&String::from("task-two")));
    }

    #[test]
    fn task_scan_constructs_multiple_across_directories() {
        let single_manifest: PathBuf = [env!("CARGO_MANIFEST_DIR"), "test-data", "single-task"]
            .iter()
            .collect();
        let multiple_manifest: PathBuf = [
            env!("CARGO_MANIFEST_DIR"),
            "test-data",
            "multiple-task-workspace",
        ]
        .iter()
        .collect();

        let tasks =
            Task::scan_directories(vec![single_manifest.as_path(), multiple_manifest.as_path()]);
        assert_eq!(tasks.len(), 3);

        let names: Vec<&String> = tasks.iter().map(|t| &t.config.task_name).collect();
        assert!(names.contains(&&String::from("single-task")));
        assert!(names.contains(&&String::from("task-one")));
        assert!(names.contains(&&String::from("task-two")));
    }
}

R polymorph-builder/test-data/task-and-platform-workspace/Cargo.toml => polymorph-builder/test-data/multiple-platform-workspace/Cargo.toml +1 -2
@@ 1,6 1,5 @@
[workspace]
members = [
    "platform-one",
    "task-one",
    "task-two"
    "platform-two",
]

R polymorph-builder/test-data/task-and-platform-workspace/platform-one/Cargo.toml => polymorph-builder/test-data/multiple-platform-workspace/platform-one/Cargo.toml +0 -0
R polymorph-builder/test-data/task-and-platform-workspace/platform-one/x86_64-none.json => polymorph-builder/test-data/multiple-platform-workspace/platform-one/x86_64-none.json +0 -0
A polymorph-builder/test-data/multiple-platform-workspace/platform-two/Cargo.toml => polymorph-builder/test-data/multiple-platform-workspace/platform-two/Cargo.toml +3 -0
@@ 0,0 1,3 @@
[package.metadata.polymorph-platform]
platform-name = "platform-two"
default-target = "x86_64-none.json"

A polymorph-builder/test-data/multiple-platform-workspace/platform-two/x86_64-none.json => polymorph-builder/test-data/multiple-platform-workspace/platform-two/x86_64-none.json +15 -0
@@ 0,0 1,15 @@
{
  "llvm-target": "x86_64-unknown-none",
  "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
  "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",
  "panic-strategy": "abort",
  "disable-redzone": true,
  "features": "-mmx,-sse,-sse2,+soft-float"
}

D polymorph-builder/test-data/multiple-task-workspace/Cargo.toml => polymorph-builder/test-data/multiple-task-workspace/Cargo.toml +0 -5
@@ 1,5 0,0 @@
[workspace]
members = [
    "task-one",
    "task-two"
]

D polymorph-builder/test-data/multiple-task-workspace/task-one/Cargo.toml => polymorph-builder/test-data/multiple-task-workspace/task-one/Cargo.toml +0 -4
@@ 1,4 0,0 @@
[package.metadata.polymorph-task]
task-name = "task-one"
supported-architectures = ["amd64"]
task-depends = ["task-two"]

D polymorph-builder/test-data/multiple-task-workspace/task-two/Cargo.toml => polymorph-builder/test-data/multiple-task-workspace/task-two/Cargo.toml +0 -4
@@ 1,4 0,0 @@
[package.metadata.polymorph-task]
task-name = "task-two"
supported-architectures = ["amd64"]
task-depends = []

D polymorph-builder/test-data/single-task/Cargo.toml => polymorph-builder/test-data/single-task/Cargo.toml +0 -4
@@ 1,4 0,0 @@
[package.metadata.polymorph-task]
task-name = "single-task"
supported-architectures = ["amd64"]
task-depends = []

D polymorph-builder/test-data/task-and-platform-workspace/task-one/Cargo.toml => polymorph-builder/test-data/task-and-platform-workspace/task-one/Cargo.toml +0 -4
@@ 1,4 0,0 @@
[package.metadata.polymorph-task]
task-name = "task-one"
supported-architectures = ["amd64"]
task-depends = ["task-two"]

D polymorph-builder/test-data/task-and-platform-workspace/task-two/Cargo.toml => polymorph-builder/test-data/task-and-platform-workspace/task-two/Cargo.toml +0 -4
@@ 1,4 0,0 @@
[package.metadata.polymorph-task]
task-name = "task-two"
supported-architectures = ["amd64"]
task-depends = []

D polymorph-kapi-proc/Cargo.toml => polymorph-kapi-proc/Cargo.toml +0 -13
@@ 1,13 0,0 @@
[package]
name = "polymorph-kapi-proc"
version = "1.0.0-dev.1"
authors = ["polymorphOS contributors"]
edition = "2018"
publish = false

[dependencies]
quote = "1"
syn = { version = "1", features = [ "full", "parsing", "printing", "proc-macro" ], default-features = false }

[lib]
proc-macro = true

D polymorph-kapi-proc/src/lib.rs => polymorph-kapi-proc/src/lib.rs +0 -236
@@ 1,236 0,0 @@
//! polymorphOS Kernel API task construction proc-macro

#![recursion_limit = "512"]
#![feature(proc_macro_hygiene)]

extern crate proc_macro;
use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
use syn::{
    self,
    parse::{Parse, ParseStream, Result as ParseResult},
    spanned::Spanned,
};

struct PmosTask {
    name: syn::Ident,
    data_ty: syn::Type,
    init_fn: syn::Expr,
}

impl Parse for PmosTask {
    fn parse(input: ParseStream) -> ParseResult<Self> {
        let name: syn::Ident = input.parse()?;
        input.parse::<syn::Token![,]>()?;
        let data_ty: syn::Type = input.parse()?;
        input.parse::<syn::Token![,]>()?;
        let init_fn: syn::Expr = input.parse()?;

        Ok(PmosTask {
            name,
            data_ty,
            init_fn,
        })
    }
}

/// Kernel task constructor.
///
/// Constructs a kernel task with the given name and data type, creates hooks
/// for the kernel to interact with the task, and registers the task with the
/// kernel. Additionally, constructs a global memory allocator for the task.
///
/// # Example
///
/// ```ignore
/// # extern crate polymorph_kapi;
/// # #[macro_use] extern crate polymorph_kapi_proc;
/// pub struct TestDataType(usize);
/// impl Default for TestDataType {
///     fn default() -> TestDataType {
///         TestDataType(0)
///     }
/// }
///
/// pub fn initialize(task: &mut PolymorphOSTask<TestDataType>) {
///     task.with_task_data(|d| {
///         d.0 = 12345usize;
///     });
/// }
///
/// pmos_task!(pmtask_test, TestDataType, initialize);
/// ```
///
/// # Parameters
///
/// ```ignore
/// pmos_task!(task_name, TaskDataType, task_initialization_fn);
/// ```
#[proc_macro]
pub fn pmos_task(input: TokenStream) -> TokenStream {
    let PmosTask {
        name,
        data_ty,
        init_fn,
    } = syn::parse_macro_input!(input as PmosTask);

    let name_upper = format!("{}", name).to_ascii_uppercase();

    let task_name_bytestr = syn::parse_str::<syn::LitByteStr>(format!("b\"{}\"", name).as_ref())
        .expect("Unable to create task name bytestring");

    let container_ident =
        syn::parse_str::<syn::Ident>(format!("TaskContainer__{}", name_upper).as_ref())
            .expect("Unable to create task container ident");

    let task_ident = syn::parse_str::<syn::Ident>(format!("task__{}", name).as_ref())
        .expect("Unable to create task ident");

    let task_initfn_ident = syn::parse_str::<syn::Ident>(format!("taskinitfn__{}", name).as_ref())
        .expect("Unable to create task init function ident");

    let q_assert_sized_sync = quote_spanned! { data_ty.span() =>
        struct _AssertSync where #data_ty: core::marker::Sync;
        struct _AssertSized where #data_ty: core::marker::Sized;
    };

    let q_container = quote! {
        #[doc="Static container for this task's `PolymorphOSTask` structure"]
        #[allow(non_camel_case_types)]
        pub struct #container_ident<'a>(core::cell::UnsafeCell<polymorph_kapi::task::PolymorphOSTask<'a, #data_ty>>);

        unsafe impl<'a> Sync for #container_ident<'a> {}
        unsafe impl<'a> Send for #container_ident<'a> {}

        impl<'a> #container_ident<'a> {
            /// Create a new container
            pub const fn new(name: &'static [u8]) -> #container_ident<'a> {
                let task: polymorph_kapi::task::PolymorphOSTask<'a, #data_ty> =
                    polymorph_kapi::task::PolymorphOSTask::new(name);

                #container_ident(core::cell::UnsafeCell::new(task))
            }

            /// Get a mutable reference to the inner task
            pub fn get<'s>(&self) -> &'s mut polymorph_kapi::task::PolymorphOSTask<'a, #data_ty> {
                unsafe { &mut *self.0.get() }
            }
        }
    };

    let q_task = quote! {
        #[doc="`PolymorphOSTask` structure for this task, wrapped in a static container"]
        #[used]
        #[no_mangle]
        pub static #task_ident
        :
        #container_ident<'static> = #container_ident::new(#task_name_bytestr);
    };

    let task_hookstub_poll_ident =
        syn::parse_str::<syn::Ident>(format!("taskhookstubfn_poll__{}", name).as_ref())
            .expect("Unable to create task poll hook function ident");

    let task_hookstub_act_ident =
        syn::parse_str::<syn::Ident>(format!("taskhookstubfn_act__{}", name).as_ref())
            .expect("Unable to create task act hook function ident");

    let task_hookstub_msg_ident =
        syn::parse_str::<syn::Ident>(format!("taskhookstubfn_msg__{}", name).as_ref())
            .expect("Unable to create task msg hook function ident");

    let q_hook_stubs = quote! {
        #[used]
        #[no_mangle]
        #[doc(hidden)]
        pub static #task_hookstub_poll_ident
        :
        extern "C" fn() -> bool = {
            extern "C" fn hookstub_poll() -> bool {
                #task_ident.get().perform_poll()
            };

            hookstub_poll
        };

        #[used]
        #[no_mangle]
        #[doc(hidden)]
        pub static #task_hookstub_act_ident
        :
        extern "C" fn() = {
            extern "C" fn hookstub_act() {
                #task_ident.get().perform_act()
            };

            hookstub_act
        };

        #[used]
        #[no_mangle]
        #[doc(hidden)]
        pub static #task_hookstub_msg_ident
        :
        extern "C" fn(
            topic: *const u8,
            topic_len: usize,
            subject: *const u8,
            subject_len: usize,
            data: *const u8,
            data_len: usize,
        ) = {
            extern "C" fn hookstub_msg(
                topic: *const u8,
                topic_len: usize,
                subject: *const u8,
                subject_len: usize,
                data: *const u8,
                data_len: usize,
            ) {
                use polymorph_kapi::polymorph_helpers::byteclone;

                let topic_str = byteclone::bc_string(topic, topic_len);
                let subject_vec = byteclone::bc_vec(subject, subject_len);
                let data_vec = byteclone::bc_vec(data, data_len);

                #task_ident.get().perform_msg(topic_str, subject_vec, data_vec);
            };

            hookstub_msg
        };
    };

    let q_task_init = quote! {
        #[used]
        #[no_mangle]
        #[doc(hidden)]
        pub static #task_initfn_ident
        :
        extern "C" fn() = {
            extern "C" fn task_init() {
                // Get mutable task reference
                let mut task = #task_ident.get();

                // Get task-provided init function
                let task_init_fn: fn(&mut polymorph_kapi::task::PolymorphOSTask<#data_ty>) = #init_fn;

                // Initialize task - first the task structure initialization
                // and then the task-provided init function
                task.initialize();
                (task_init_fn)(task);
            };

            task_init
        };
    };

    let expanded = quote! {
        #q_assert_sized_sync
        #q_container
        #q_task
        #q_hook_stubs
        #q_task_init
    };

    TokenStream::from(expanded)
}

D polymorph-kapi/Cargo.toml => polymorph-kapi/Cargo.toml +0 -13
@@ 1,13 0,0 @@
[package]
name = "polymorph-kapi"
version = "1.0.0-dev.1"
authors = ["polymorphOS contributors"]
edition = "2018"
publish = false

[dependencies]
polymorph-helpers = { path = "../polymorph-helpers" }
cfg-if = "0.1.10"
spin = "0.5.2"
cty = "0.2"
lazy_static = { version = "1.4.0", features = [ "spin_no_std" ] }

D polymorph-kapi/README.md => polymorph-kapi/README.md +0 -33
@@ 1,33 0,0 @@
# polymorph-kapi

The polymorphOS Kernel API.

This crate is used by drivers (and other privileged tasks) to communicate
across the FFI boundary with the core kernel.

## Usage

For tasks contained within the main polymorphOS repository, add the following
to the module's `Cargo.toml`:

```toml
[dependencies]
polymorph-kapi = { path = "../polymorph-kapi" }
polymorph-kapi-proc = { path = "../polymorph-kapi-proc" }
```

For tasks in external repositories:

```toml
[dependencies]
polymorph-kapi = { git = "https://gitlab.com/alxce/polymorphos.git" }
polymorph-kapi-proc = { git = "https://gitlab.com/alxce/polymorphos.git" }
```

## License

`polymorph-kapi` is licensed under the MIT License, the same license as
the rest of polymorphOS. 

You can find this license in the `LICENSE` file in the top level of this
repository.

D polymorph-kapi/src/lib.rs => polymorph-kapi/src/lib.rs +0 -29
@@ 1,29 0,0 @@
//! The polymorphOS Kernel API.
//!
//! This crate is used by drivers (and other privileged tasks) to communicate
//! across the FFI boundary with the core kernel.
//!
//! Currently, this crate uses direct C function calls into the kernel,
//! however this will change in the future, as drivers (and other such tasks)
//! will be run in user-space (x86 ring 3) rather than kernel-space (x86
//! ring 0).

#![no_std]
#![feature(const_fn)]
#![feature(alloc_prelude, alloc_error_handler)]
#![feature(panic_info_message)]
#![allow(unused_imports)]

#[cfg(not(test))]
extern crate core;
#[cfg(test)]
extern crate std as core;
#[macro_use]
extern crate alloc;

pub extern crate polymorph_helpers;

mod panic;
pub mod prelude;
pub mod task;
pub use self::panic::*;

D polymorph-kapi/src/panic.rs => polymorph-kapi/src/panic.rs +0 -57
@@ 1,57 0,0 @@
//! Panic handler

use alloc::alloc::Layout;
use alloc::prelude::v1::*;
use core::panic::PanicInfo;

extern "C" {
    #[doc(hidden)]
    fn pmos_kapi_panic(
        message: *const u8,
        message_len: usize,
        file: *const u8,
        file_len: usize,
        line: u32,
    ) -> !;
}

/// Panic handler function that passes panic info through the kernel API.
#[allow(dead_code)]
#[cfg_attr(target_os = "polymorphos", panic_handler)]
pub fn panic_handler(info: &PanicInfo<'_>) -> ! {
    let mut file = String::from("(unknown)");
    let mut line = 0u32;
    if let Some(loc) = info.location() {
        file = format!("{}", loc.file());
        line = loc.line();
    }

    let file_bytes = file.as_bytes();

    let message = match info.message() {
        Some(msg) => format!("{}", msg),
        None => format!("(unknown)"),
    };

    let message_bytes = message.as_bytes();

    unsafe {
        pmos_kapi_panic(
            message_bytes.as_ptr(),
            message_bytes.len(),
            file_bytes.as_ptr(),
            file_bytes.len(),
            line,
        );
    }
}

/// Allocation error handler function.
///
/// Internally this just panics with an "Allocation error" message containing
/// a debug format of the layout that errored.
#[allow(dead_code)]
#[cfg_attr(target_os = "polymorphos", alloc_error_handler)]
pub fn alloc_error_handler(layout: Layout) -> ! {
    panic!("Allocation error: {:?}", layout);
}

D polymorph-kapi/src/prelude.rs => polymorph-kapi/src/prelude.rs +0 -3
@@ 1,3 0,0 @@
//! The polymorphOS Kernel API Prelude

pub use crate::task::PolymorphOSTask;

D polymorph-kapi/src/task.rs => polymorph-kapi/src/task.rs +0 -148
@@ 1,148 0,0 @@
//! Kernel task handling.
//!
//! To construct tasks, you will generally want to use the [`pmos_task`]
//! macro, as it automatically creates and registers callbacks with the
//! kernel.
//!
//! [`pmos_task`]: ../macro.pmos_task.html

use alloc::prelude::v1::*;
use core::fmt::{self, Debug};
use core::ops::DerefMut;
use spin::Mutex;

/// A polymorphOS kernel task.
pub struct PolymorphOSTask<'a, T: Default> {
    task: &'static [u8],
    task_data: Option<Mutex<T>>,
    fn_poll: Option<Box<dyn Fn(&mut T) -> bool + 'a>>,
    fn_act: Option<Box<dyn Fn(&mut T) + 'a>>,
    fn_msg: Option<Box<dyn Fn(&mut T, String, Vec<u8>, Vec<u8>) + 'a>>,
}

impl<'a, T: Default> PolymorphOSTask<'a, T> {
    /// Create a new task.
    pub const fn new(task: &'static [u8]) -> Self {
        Self {
            task,
            task_data: None,
            fn_poll: None,
            fn_act: None,
            fn_msg: None,
        }
    }

    /// Get this task's name.
    pub fn task_name(&self) -> &'static [u8] {
        self.task
    }

    /// Initialize this task.
    ///
    /// Creates an instance of this task's data type via the `Default` trait.
    ///
    /// Note that none of the functions stored on this task will execute until
    /// the task has been initialized.
    pub fn initialize(&mut self) {
        self.task_data = Some(Mutex::new(Default::default()));
    }

    /// Execute the given closure with a mutable reference to the task data
    /// for this task, returning the value of the closure.
    ///
    /// > **Note**: This function locks the task data structure so that only
    /// > the closure passed to this function can access it. This can cause
    /// > a deadlock if, for example, the task is mid-execution when a
    /// > message is received. The kernel will do everything it can to stop
    /// > this from happening.
    pub fn with_task_data<A>(&self, mut with_task_data: impl FnMut(&mut T) -> A) -> Option<A> {
        if let Some(ref task_data) = self.task_data {
            let mut td = task_data.lock();
            let out = (with_task_data)(td.deref_mut());

            Some(out)
        } else {
            None
        }
    }

    /// Sets the function to be called to poll whether this task should be
    /// considered for execution by the kernel.
    ///
    /// Stores the given function as this task's polling function. Future
    /// calls to [`perform_poll`] on this task will call that stored function.
    ///
    /// [`perform_poll`]: #method.perform_poll
    pub fn set_poll(&mut self, fn_poll: impl Fn(&mut T) -> bool + 'a) {
        self.fn_poll = Some(Box::new(fn_poll));
    }

    /// Calls the function stored by [`set_poll`], or returns `true` if no
    /// function has been set.
    ///
    /// [`set_poll`]: #method.set_poll
    pub fn perform_poll(&self) -> bool {
        if let Some(ref fn_poll) = self.fn_poll {
            if let Some(pollres) = self.with_task_data(|d| (fn_poll)(d)) {
                pollres
            } else {
                true
            }
        } else {
            true
        }
    }

    /// Sets the function to be called to execute this task.
    ///
    /// Stores the given function as this task's action function. Future
    /// calls to [`perform_act`] on this task will call that stored function.
    ///
    /// [`perform_act`]: #method.perform_act
    pub fn set_act(&mut self, fn_act: impl Fn(&mut T) + 'a) {
        self.fn_act = Some(Box::new(fn_act));
    }

    /// Calls the function stored by [`set_act`], or does nothing if no
    /// function has been set.
    ///
    /// [`set_act`]: #method.set_act
    pub fn perform_act(&self) {
        if let Some(ref fn_act) = self.fn_act {
            self.with_task_data(|d| {
                (fn_act)(d);
            });
        }
    }

    /// Sets the function to be called for incoming messages from the kernel
    /// destined for this task.
    ///
    /// Stores the given function as this task's messaging function. Future
    /// calls to [`perform_msg`] on this task will call that stored function.
    ///
    /// [`perform_msg`]: #method.perform_msg
    pub fn set_msg(&mut self, fn_msg: impl Fn(&mut T, String, Vec<u8>, Vec<u8>) + 'a) {
        self.fn_msg = Some(Box::new(fn_msg));
    }

    /// Calls the function stored by [`set_msg`], or does nothing if no
    /// function has been set.
    ///
    /// [`set_msg`]: #method.set_msg
    pub fn perform_msg(&self, topic: String, subject: Vec<u8>, data: Vec<u8>) {
        if let Some(ref fn_msg) = self.fn_msg {
            self.with_task_data(|d| {
                (fn_msg)(d, topic.clone(), subject.clone(), data.clone());
            });
        }
    }
}

impl<'a, T: Default> Debug for PolymorphOSTask<'a, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("PolymorphOSTask")
            .field(&self.task_name())
            .finish()
    }
}