0.3.0: fix linkage in C mode

This commit is contained in:
Adam 2025-08-17 20:01:04 +01:00
parent 5657325d97
commit 987152f14f
7 changed files with 108 additions and 80 deletions

2
Cargo.lock generated
View File

@ -4,7 +4,7 @@ version = 4
[[package]] [[package]]
name = "bin2hpp" name = "bin2hpp"
version = "0.2.5" version = "0.3.0"
dependencies = [ dependencies = [
"thiserror", "thiserror",
] ]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bin2hpp" name = "bin2hpp"
version = "0.2.5" version = "0.3.0"
authors = ["Adam Macdonald"] authors = ["Adam Macdonald"]
edition = "2024" edition = "2024"
license = "GPL-3.0-only" license = "GPL-3.0-only"

View File

@ -17,20 +17,35 @@ The performance (but potentially not the executable size) can be further optimis
`$ bin2hpp -i my_library.dll -o c_header.h` will generate a `c_header.h` file in your current working directory containing something similar to the following: `$ bin2hpp -i my_library.dll -o c_header.h` will generate a `c_header.h` file in your current working directory containing something similar to the following:
```c ```c
// Generated by bin2hpp 0.2.5 // Generated by bin2hpp 0.3.0
#ifndef C_HEADER_H #ifdef MY_LIBRARY_DLL_INIT
#define C_HEADER_H
#define MY_LIBRARY_DLL_LEN 9728 #define MY_LIBRARY_DLL_LEN 9728
const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN] = {77,90,144,0,3,0,0,0,4, ...}; const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN] = {77,90,144,0,3,0,0,0,4, ...};
#else #else
#define MY_LIBRARY_DLL_LEN 9728
extern const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN]; extern const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN];
#endif #endif
``` ```
Note how in **C mode** one must `#define MY_LIBRARY_DLL_INIT`, or whatever the corresponding implementation macro is for your generated header file, in exactly _one_ C source file (`*.c`, not in a `*.h` file).
Example:
`main.c`
```c
// In any other C source files other than the first: do not redefine
#define MY_LIBRARY_DLL_INIT
#include "c_header.h"
int main() {
return MY_LIBRARY_DLL_LEN;
}
```
Alternatively, in **C++ mode**: `bin2hpp -i my_library.dll -o cpp_header.hpp --cpp --constexpr --stdarray --i8` Alternatively, in **C++ mode**: `bin2hpp -i my_library.dll -o cpp_header.hpp --cpp --constexpr --stdarray --i8`
```cpp ```cpp
// Generated by bin2hpp 0.2.5 // Generated by bin2hpp 0.3.0
#if !defined(CPP_HEADER_HPP) #if !defined(CPP_HEADER_HPP)
#define CPP_HEADER_HPP #define CPP_HEADER_HPP
#include <cstdint> #include <cstdint>

View File

@ -22,7 +22,8 @@ pub struct Config {
pub(crate) constness: Constness, pub(crate) constness: Constness,
pub(crate) symbol_name: String, pub(crate) symbol_name: String,
pub(crate) namespace: Option<String>, pub(crate) namespace: Option<String>,
pub(crate) guard_name: String, pub(crate) impl_macro: String,
pub(crate) header_guard: String,
pub(crate) container_type: ContainerType, pub(crate) container_type: ContainerType,
} }
@ -56,6 +57,54 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
} }
}; };
let symbol_name = match &args.symbol_name {
Some(s) => {
if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
s.to_string()
} else {
return Err(ProgramConfigError::InvalidSymbolName(s.to_string()));
}
}
_ => {
let mut stem: String = match args.input_file_path.file_stem() {
Some(s) => s.to_string_lossy().to_string(),
None => {
return Err(ProgramConfigError::InvalidPath(
args.input_file_path.to_string_lossy().to_string(),
));
}
};
stem.retain(|c| c.is_ascii_alphanumeric() || c == '_');
let mut ext: String = match args.input_file_path.extension() {
Some(s) => format!("_{}", s.to_string_lossy()),
None => "".to_string(),
};
ext.retain(|c| c.is_ascii_alphanumeric() || c == '_');
format!("{}{}", stem.to_ascii_uppercase(), ext.to_ascii_uppercase())
}
};
let header_guard = match output_path.file_stem() {
Some(stem) => {
format!(
"{}_{}",
stem.to_string_lossy().to_uppercase(),
if let Some(ext) = output_path.extension() {
ext.to_string_lossy().to_uppercase()
} else {
"".into()
}
)
}
None => {
return Err(ProgramConfigError::InvalidPath(
output_path.to_string_lossy().to_string(),
));
}
};
Ok(Config { Ok(Config {
input_file_path: args.input_file_path.clone(), input_file_path: args.input_file_path.clone(),
output_file_path: output_path.clone(), output_file_path: output_path.clone(),
@ -67,34 +116,7 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
}, },
language: args.language, language: args.language,
constness: args.constness, constness: args.constness,
symbol_name: match &args.symbol_name { symbol_name: symbol_name.clone(),
Some(s) => {
if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
s.to_string()
} else {
return Err(ProgramConfigError::InvalidSymbolName(s.to_string()));
}
}
_ => {
let mut stem: String = match args.input_file_path.file_stem() {
Some(s) => s.to_string_lossy().to_string(),
None => {
return Err(ProgramConfigError::InvalidPath(
args.input_file_path.to_string_lossy().to_string(),
));
}
};
stem.retain(|c| c.is_ascii_alphanumeric() || c == '_');
let mut ext: String = match args.input_file_path.extension() {
Some(s) => format!("_{}", s.to_string_lossy()),
None => "".to_string(),
};
ext.retain(|c| c.is_ascii_alphanumeric() || c == '_');
format!("{}{}", stem.to_ascii_uppercase(), ext.to_ascii_uppercase())
}
},
namespace: match &args.namespace { namespace: match &args.namespace {
Some(ns) => { Some(ns) => {
if ns.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { if ns.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
@ -105,7 +127,7 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
} }
_ => None, _ => None,
}, },
guard_name: match &args.guard_name { impl_macro: match &args.impl_macro_name {
Some(guard) => { Some(guard) => {
if guard.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { if guard.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
guard.clone() guard.clone()
@ -114,29 +136,10 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
} }
} }
_ => { _ => {
let mut stem: String = match output_path.file_stem() { format!("{}_INIT", symbol_name.to_uppercase())
Some(s) => s.to_string_lossy().to_ascii_uppercase(),
None => {
return Err(ProgramConfigError::InvalidPath(
args.input_file_path.to_string_lossy().to_string(),
));
}
};
stem.retain(|c| c.is_ascii_alphanumeric() || c == '_');
let mut ext: String = match output_path.extension() {
Some(s) => s.to_string_lossy().to_ascii_uppercase(),
None => {
return Err(ProgramConfigError::InvalidPath(
args.input_file_path.to_string_lossy().to_string(),
));
}
};
ext.retain(|c| c.is_ascii_alphanumeric() || c == '_');
format!("{}_{}", stem, ext)
} }
}, },
header_guard: header_guard,
container_type: args.container_type, container_type: args.container_type,
}) })
} }

View File

@ -81,7 +81,7 @@ pub struct CliArgs {
pub(crate) constness: Constness, pub(crate) constness: Constness,
pub(crate) symbol_name: Option<String>, pub(crate) symbol_name: Option<String>,
pub(crate) namespace: Option<String>, pub(crate) namespace: Option<String>,
pub(crate) guard_name: Option<String>, pub(crate) impl_macro_name: Option<String>,
pub(crate) container_type: ContainerType, pub(crate) container_type: ContainerType,
} }
@ -123,10 +123,10 @@ pub fn parse_cli_args(args: &[String]) -> Result<CliArgs, CliParseError> {
None => None, None => None,
}; };
let guard_name = match args.iter().position(|s| *s == "--guard") { let impl_macro_name = match args.iter().position(|s| *s == "--impl") {
Some(idx) => match args.get(idx + 1) { Some(idx) => match args.get(idx + 1) {
Some(s) => Some(s), Some(s) => Some(s),
None => return Err(CliParseError::MissingOperand("--guard")), None => return Err(CliParseError::MissingOperand("--impl")),
}, },
None => None, None => None,
}; };
@ -237,7 +237,7 @@ pub fn parse_cli_args(args: &[String]) -> Result<CliArgs, CliParseError> {
constness: constness, constness: constness,
symbol_name: symbol_name.cloned(), symbol_name: symbol_name.cloned(),
namespace: namespace.cloned(), namespace: namespace.cloned(),
guard_name: guard_name.cloned(), impl_macro_name: impl_macro_name.cloned(),
container_type: container_type, container_type: container_type,
}); });
} }

View File

@ -28,8 +28,8 @@ OUTPUT OPTIONS:
--symname NAME Symbol name --symname NAME Symbol name
--namespace NAME Namespace in which the symbol will exist (C++ mode) --namespace NAME Namespace in which the symbol will exist (C++ mode)
--guard NAME Preprocessor macro to use for the header guard (#ifndef ...) --impl NAME Preprocessor macro to use for the implementation guard
(default: derive from output filename) in C mode (default: derive from symbol name)
CONTAINER TYPES: CONTAINER TYPES:

View File

@ -33,18 +33,17 @@ pub fn header(config: &Config, in_data: &[u8]) -> Result<String, HeaderGeneratio
write!( write!(
buf, buf,
"#if !defined({}){}", "#if !defined({}){}",
config.guard_name, config.line_ending config.header_guard, config.line_ending
) )
.unwrap(); .unwrap();
write!(buf, "#define {}{}", config.header_guard, config.line_ending).unwrap();
} else { } else {
// C style // C style
write!(buf, "#ifndef {}{}", config.guard_name, config.line_ending).unwrap(); write!(buf, "#ifdef {}{}", config.impl_macro, config.line_ending).unwrap();
} }
write!(buf, "#define {}{}", config.guard_name, config.line_ending).unwrap();
// Includes & main content // Includes & main content
with_includes(config, &mut buf);
with_contents(config, in_data, &mut buf)?; with_contents(config, in_data, &mut buf)?;
// End header guard // End header guard
@ -59,6 +58,21 @@ pub fn with_contents(
in_data: &[u8], in_data: &[u8],
buf: &mut String, buf: &mut String,
) -> Result<(), HeaderGenerationError> { ) -> Result<(), HeaderGenerationError> {
with_includes(config, buf);
let length: usize = match config.container_type {
ContainerType::CString => in_data.len() + 1, // +1 for NUL terminator
_ => in_data.len(),
};
write!(
buf,
"{}{}",
length_define(config, length),
config.line_ending
)
.unwrap();
if config.language == Language::Cpp { if config.language == Language::Cpp {
// C++ mode // C++ mode
@ -73,8 +87,17 @@ pub fn with_contents(
// C mode // C mode
with_definition(config, in_data, buf)?; with_definition(config, in_data, buf)?;
write!(buf, "#else{}", config.line_ending).unwrap(); write!(buf, "#else{}", config.line_ending).unwrap();
with_includes(config, buf); with_includes(config, buf);
write!(
buf,
"{}{}",
length_define(config, length),
config.line_ending
)
.unwrap();
with_declaration(config, buf); with_declaration(config, buf);
} }
@ -154,21 +177,8 @@ pub fn with_definition(
in_data: &[u8], in_data: &[u8],
buf: &mut String, buf: &mut String,
) -> Result<(), HeaderGenerationError> { ) -> Result<(), HeaderGenerationError> {
let length: usize = match config.container_type {
ContainerType::CString => in_data.len() + 1, // +1 for NUL terminator
_ => in_data.len(),
};
let modifiers = definition_modifiers(config); let modifiers = definition_modifiers(config);
write!(
buf,
"{}{}",
length_define(config, length),
config.line_ending
)
.unwrap();
write!( write!(
buf, buf,
"{}{} = {};{}", "{}{} = {};{}",