Skip to content

Commit

Permalink
wip for #10
Browse files Browse the repository at this point in the history
#10

Signed-off-by: Sami Salonen <ssalonen@gmail.com>
  • Loading branch information
ssalonen committed Jan 29, 2022
1 parent 8f205fc commit e5fc2b1
Show file tree
Hide file tree
Showing 12 changed files with 3,704 additions and 63 deletions.
1 change: 1 addition & 0 deletions abis.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ABIS="4.0.5 5.0.0 6.0.2"
121 changes: 95 additions & 26 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
use copy_dir::copy_dir;
use std::env;
use std::fs;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::process::Command;

const MIN_LIBCEC_VERSION: &str = "4.0.0";

const P8_PLATFORM_DIR_ENV: &str = "p8-platform_DIR";
const LIBCEC_BUILD: &str = "libcec_build";
const PLATFORM_BUILD: &str = "platform_build";
const LIBCEC_SRC: &str = "vendor";

enum CecVersion {
V4,
V5,
V6,
}

impl CecVersion {
fn major(&self) -> u32 {
match self {
&Self::V4 => 4,
&Self::V5 => 5,
&Self::V6 => 6,
}
}
}

// libcec versions that are supported when linking dynamically. In preferce order
const CEC_MAJOR_VERSIONS: [CecVersion; 3] = [CecVersion::V6, CecVersion::V5, CecVersion::V4];

fn prepare_vendored_build(dst: &Path) {
let dst_src = dst.join(LIBCEC_SRC);
if dst_src.exists() && dst_src.is_dir() {
Expand Down Expand Up @@ -71,36 +90,60 @@ fn compile_vendored_libcec(dst: &Path) {
.expect("failed to make libcec!");
}

fn libcec_installed_smoke_test() -> bool {
fn libcec_installed_smoke_test() -> Result<CecVersion, ()> {
let compiler = cc::Build::new().get_compiler();
let compiler_path = compiler.path();
let mut cc_cmd = Command::new(compiler_path);
let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
cc_cmd
.arg("src/smoke.c")
.arg("-o")
.arg(dst.join("smoke_out"))
.arg("-lcec");
if let Ok(status) = cc_cmd.status() {
if status.success() {
return true;
for abi in CEC_MAJOR_VERSIONS {
cc_cmd
.arg(format!("src/smoke_abi{}.c", abi.major()))
.arg("-o")
.arg(dst.join("smoke_out"))
.arg("-lcec");
if let Ok(status) = cc_cmd.status() {
if status.success() {
return Ok(abi);
}
}
}

Err(())
}

fn libcec_installed_pkg_config() -> Result<CecVersion, ()> {
for abi in CEC_MAJOR_VERSIONS {
if pkg_config::Config::new()
.atleast_version(&abi.major().to_string())
.probe("libcec")
.is_ok()
{
return Ok(abi);
}
}
false
Err(())
}

fn compile_vendored() {
println!("cargo:lib_vendored=true");

let cmakelists = format!("{}/CMakeLists.txt", LIBCEC_SRC);
let libcec_git_dir = Path::new(&cmakelists);
if !libcec_git_dir.exists() {
let cmakelists = Path::new(&cmakelists);
if !cmakelists.exists() {
panic!(
"git submodules (tested {}, working dir {}) are not properly initialized! Aborting.",
libcec_git_dir.display(),
cmakelists.display(),
env::current_dir()
.expect("Unknown working directory")
.display()
)
}

println!(
"cargo:libcec_version_major={}",
parse_vendored_libcec_major_version(cmakelists)
);

let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
println!("Building libcec from local source");
prepare_vendored_build(&dst);
Expand All @@ -113,24 +156,50 @@ fn compile_vendored() {
println!("cargo:rustc-link-lib=cec");
}

fn parse_vendored_libcec_major_version(cmakelists: &Path) -> u32 {
let file = File::open(cmakelists).expect("Error opening cmakelists");
let reader = BufReader::new(file);
// Parse major version from line similar to set(LIBCEC_VERSION_MAJOR 4)
for line in reader.lines() {
let line = line.expect("Error reading cmakelists");
let mut numbers = String::new();
if line.trim().starts_with("set(LIBCEC_VERSION_MAJOR ") {
for char in line.chars() {
match char {
'0'..='9' => numbers.push(char),
_ => {}
}
}
return numbers.parse().expect("major version parse failed");
}
}
panic!("Could not parse LIBCEC_VERSION_MAJOR from cmakelists");
}

fn main() {
println!("cargo:rerun-if-changed=build.rs");
if cfg!(feature = "vendored") {
// vendored build explicitly requested. Build vendored sources
compile_vendored();
}
// Try discovery using pkg-config
else if pkg_config::Config::new()
.atleast_version(MIN_LIBCEC_VERSION)
.probe("libcec")
.is_ok()
{
// pkg-config found the package and the parameters will be used for linking
}
// Try smoke-test build using -lcec. If unsuccessful, revert to vendored sources
else if libcec_installed_smoke_test() {
println!("cargo:rustc-link-lib=cec");
} else {
else {
let version = libcec_installed_pkg_config();
if let Ok(version) = version {
// pkg-config found the package and the parameters will be used for linking
println!("cargo:libcec_version_major={}", version.major());
return;
}
// Try smoke-test build using -lcec. If unsuccessful, revert to vendored sources
let version = libcec_installed_smoke_test();
if let Ok(version) = version {
println!("cargo:rustc-link-lib=cec");
println!("cargo:libcec_version_major={}", version.major());
return;
}
// Fallback
compile_vendored();
}

// TODO: output lib version even with vendored
}
83 changes: 54 additions & 29 deletions scripts/run-bindgen/bindgen.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
#!/usr/bin/env bash
set -ex
set -xe

OUT=lib.rs
DEST_DIR=../../src/
CEC_REGEX='(libcec|cec|CEC|LIBCEC)_.*'
VENDOR_TMP=../../vendor_tmp

function generate() {
# bindgen layout tests are disabled for cross-arch compatibility
# See https://kornel.ski/rust-sys-crate Stable API guidance
# and https://users.rust-lang.org/t/what-to-do-when-bindgen-tests-fail/23822
bindgen wrapper.h -o ${OUT}.tmp \
--whitelist-type $CEC_REGEX \
--whitelist-function $CEC_REGEX \
--whitelist-var $CEC_REGEX \
--whitelist-type "$CEC_REGEX" \
--whitelist-function "$CEC_REGEX" \
--whitelist-var "$CEC_REGEX" \
--blacklist-type cec_boolean \
--no-layout-tests \
--no-prepend-enum-name \
Expand All @@ -28,30 +29,54 @@ function generate() {
--raw-line=')]' \
"$@" \
-- \
-I include_tmp
-I vendor_tmp/include
}

cp -a ../../vendor/include include_tmp
cp include_tmp/version.h.in include_tmp/version.h

LIBCEC_VERSION_MAJOR=$(grep -E -o 'set\(LIBCEC_VERSION_MAJOR [^)]' ../../vendor/CMakeLists.txt|cut -d ' ' -f2)
LIBCEC_VERSION_MINOR=$(grep -E -o 'set\(LIBCEC_VERSION_MINOR [^)]' ../../vendor/CMakeLists.txt|cut -d ' ' -f2)
LIBCEC_VERSION_PATCH=$(grep -E -o 'set\(LIBCEC_VERSION_PATCH [^)]' ../../vendor/CMakeLists.txt|cut -d ' ' -f2)
sed -i s/@LIBCEC_VERSION_MAJOR@/$LIBCEC_VERSION_MAJOR/ include_tmp/version.h
sed -i s/@LIBCEC_VERSION_MINOR@/$LIBCEC_VERSION_MINOR/ include_tmp/version.h
sed -i s/@LIBCEC_VERSION_PATCH@/$LIBCEC_VERSION_PATCH/ include_tmp/version.h

# Generate version with enums, and capture the enum definitions
generate --rustified-enum $CEC_REGEX
./sed_bindings.py ${OUT}.tmp ${OUT} --outfile_enum ${OUT}.enum
# Generate (safer) version without enums
generate --constified-enum $CEC_REGEX
./sed_bindings.py ${OUT}.tmp ${OUT}

# Copy enums to cec-rs/src/ crate
cp ${OUT}.enum ../../../cec-rs/src/enums.rs

# Cleanup
rm ${OUT}.tmp ${OUT}.enum
mv ${OUT} ${DEST_DIR}/${OUT}
rm -rf include_tmp

source ../../abis.env
#git clone --recursive git@github.com:Pulse-Eight/libcec.git $VENDOR_TMP

# TODO: the git checkouts do not work as expected. Perhaps due to submodules??? vendor_tmp/.git 'points' to main git / conflicting with vendor

cp -a ../../vendor $VENDOR_TMP
for ABI in $ABIS; do
echo "Generating bindings for ABI=$ABI"
ABI_MAJOR=$(echo "$ABI"|cut -d '.' -f1)

git -C $VENDOR_TMP checkout "libcec-$ABI"
cp $VENDOR_TMP/include/version.h.in $VENDOR_TMP/include/version.h

LIBCEC_VERSION_MAJOR=$(grep -E -o 'set\(LIBCEC_VERSION_MAJOR [^)]' $VENDOR_TMP/CMakeLists.txt|cut -d ' ' -f2)
LIBCEC_VERSION_MINOR=$(grep -E -o 'set\(LIBCEC_VERSION_MINOR [^)]' $VENDOR_TMP/CMakeLists.txt|cut -d ' ' -f2)
LIBCEC_VERSION_PATCH=$(grep -E -o 'set\(LIBCEC_VERSION_PATCH [^)]' $VENDOR_TMP/CMakeLists.txt|cut -d ' ' -f2)

if [[ "$LIBCEC_VERSION_MAJOR" != "$ABI_MAJOR" ]]; then
echo "LIBCEC_VERSION_MAJOR ($LIBCEC_VERSION_MAJOR) did not match expected ABI_MAJOR ($ABI_MAJOR)"
exit 1
fi

sed -i s/@LIBCEC_VERSION_MAJOR@/"$LIBCEC_VERSION_MAJOR"/ $VENDOR_TMP/include/version.h
sed -i s/@LIBCEC_VERSION_MINOR@/"$LIBCEC_VERSION_MINOR"/ $VENDOR_TMP/include/version.h
sed -i s/@LIBCEC_VERSION_PATCH@/"$LIBCEC_VERSION_PATCH"/ $VENDOR_TMP/include/version.h

rg CEC_VERSION_ $VENDOR_TMP/include/cectypes.h

# Generate version with enums, and capture the enum definitions
generate --rustified-enum "$CEC_REGEX"
./sed_bindings.py ${OUT}.tmp ${OUT} --outfile_enum ${OUT}.enum
# Generate (safer) version without enums
generate --constified-enum "$CEC_REGEX"
./sed_bindings.py ${OUT}.tmp ${OUT}

# Copy enums to cec-rs/src/ crate
cp ${OUT}.enum "../../../cec-rs/src/enums$ABI_MAJOR.rs"
# libcec ABI
mv ${OUT} "${DEST_DIR}/lib_abi$ABI_MAJOR.rs"


# Cleanup
rm ${OUT}.tmp ${OUT}.enum

done

rm -rf $VENDOR_TMP
2 changes: 1 addition & 1 deletion scripts/run-bindgen/wrapper.h
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#include "../../vendor/include/cecc.h"
#include "../../vendor_tmp/include/cecc.h"
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ pub const ADAPTERTYPE_TDA995x: cec_adapter_type = 512;
pub const ADAPTERTYPE_EXYNOS: cec_adapter_type = 768;
pub const ADAPTERTYPE_AOCEC: cec_adapter_type = 1280;
pub type cec_adapter_type = u32;
pub const LIBCEC_VERSION_CURRENT: libcec_version = 262148;
pub const LIBCEC_VERSION_CURRENT: libcec_version = 262149;
#[doc = " force exporting through swig"]
pub type libcec_version = u32;
pub type cec_menu_language = [::std::os::raw::c_char; 4usize];
Expand Down
Loading

0 comments on commit e5fc2b1

Please sign in to comment.