martinboers / rust-plcnext

Rust crate for accessing I/O and other services on PLCnext Technology controllers from Phoenix Contact
3 stars 3 forks source link

Remove shell script dependencies #2

Closed hstk closed 4 years ago

hstk commented 4 years ago

Building this crate relies on a lot of environment variables passed through the SDK's environment setup script, plus an additional build script as described in the rust-sampleruntime project. Some of options in the SDK env setup are probably CPP specific. Minimizing the need for external shell scripts makes the project more modular, allow RLS to "just work" without configuration, and make life easier in general. To this end, we can move most or all of the build config to package specific build.rs scripts.

All the packages in this workspace except for plcnext-sys and plcnext can be built by passing options to cpp::Builders. For instance, assuming the 2020.0 SDK is installed under the default path, this build.rs is sufficient to make the package without sourcing anything to the path or exporting env-vars. cpp-build = 0.5 will need to be added as a build-dependency in the cargo.toml.

use std::string;

fn main() {
    let mut my_config = cpp_build::Config::new();

    println!("cargo:rustc-link-lib=Arp.Device.Interface");
    println!("cargo:rustc-link-lib=Arp.Io.Axioline");

    let sdk_lib_path = "/opt/pxc/2020.0/sysroots/cortexa9t2hf-neon-pxc-linux-gnueabi/usr/include/plcnext";
    let tool_path = "/opt/pxc/2020.0/sysroots/x86_64-pokysdk-linux/usr/bin/arm-pxc-linux-gnueabi";
    let compiler_path = tool_path.to_owned() + "/arm-pxc-linux-gnueabi-g++";
    let archiver_path = tool_path.to_owned() + "/arm-pxc-linux-gnueabi-ar";

    my_config
        .compiler(compiler_path)
        .flag("-march=armv7-a")
        .flag("-mthumb")
        .flag("-mfpu=neon")
        .flag("-mfloat-abi=hard")
        .flag("-mcpu=cortex-a9")
        .flag("--sysroot=/opt/pxc/2020.0/sysroots/cortexa9t2hf-neon-pxc-linux-gnueabi")
        .flag("-O2")
        .flag("-pipe")
        .flag("-g")
        .flag("-feliminate-unused-debug-types")
        .archiver(archiver_path)
        .include(sdk_lib_path);

    my_config.build("src/lib.rs");
}

The easiest way to get tooling locations is to link package versions to SDK versions installed in the default path (i.e. making plcnext-sys 0.3.0 rely on the 2020.0 SDK being in /opt/pxc, and 0.4.0 on 2020.3), while allowing users to pass in env-vars to override the path if needed. This should let most users just grab the SDK and crates to setup their environment.

hstk commented 4 years ago

I've got everything but some bindgen linking issues sorted; if you're willing to consider a PR I'll submit one.

martinboers commented 4 years ago

Hi Hank, thanks a million for the suggestions, this is great. It's early days for this project so there are undoubtedly many improvements like this that can be made.

I am strongly considering removing everything except plcnext-sys and plcnext from this project. Those two packages focus on the use of the PLCnext ANSI-C API, which is designed to be used by real-time control applications.

The other packages - plcnext-commons, plcnext-device and plcnext-axioline - are a first attempt to use the non-real-time PLCnext C++ APIs, and which (in this case) use the cpp and cpp-build dependencies. If we stay on this path, these three packages would need to be developed further, and in fact there are (currently) around 20 more similar packages that we could add, to give Rust users access to all the services provided by a PLCnext device. This would require a lot of development and maintenance effort, and I am now not so sure that this is the best approach for accessing these C++ APIs from Rust. The alternative we are currently looking at is to use gRPC, which would mean Rust users could access PLCnext services either locally or remotely using the relevant gRPC proto file(s) and something like grpc-rust. This would remove the requirement for Rust/C++ interop, and these packages could be removed from this project, along with their cpp and cpp-build dependencies.

Regarding the assumptions about the SDK install location - using env variables would be preferable, because there is no default SDK installation location used by Phoenix Contact - even Phoenix Contact's plcncli tool, which can be used to install and manage SDKs, does not suggest a (sensible) default SDK install location. It would be neater if we also didn't make any assumptions about this.

Regarding the proposed dependency between package versions and PLCnext SDK versions - one of the challenges that I can see with this idea is that even for version 2020.0, three variants of this SDK have been released - one for the AXC F 2152, one for the RFC 4072S and one for the AXC F 1152. There are even more PLCnext devices planned, and each will have their own variant of each new SDK. Since we cannot guess what version(s) of the SDK (including device variant) that people will be using, it doesn't make sense (IMO) to assume anything about this based on the version of the plcnext crate that they are using.

I really appreciate your input, and I would be delighted to get a PR. Thanks again for your interest.

hstk commented 4 years ago

Thanks for the clarification; I didn't realize there would be multiple toolchain versions across multiple platforms in arbitrary locations. gRPC sounds like an attractive alternative to writing and maintaining n^2 CXX APIs, and should ease porting new languages in the future.

I've forked the crate, removed the C++ API packages, and figured out the bare minimum of envvars needed to get it to build on my system. For the sake of posterity, appending the path exports from the environment setup script to my .bashrc and passing BINDGEN_EXTRA_CLANG_ARGS is all that's needed to build on my WSL1 setup. RLS now happily compiles the crate without any extra configuration, and I have tooling support again.

My issue is solved and I don't think I have anything else to contribute at the moment, so I'm closing the issue.