@@ -11,25 +11,32 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
1111use cranelift_module:: { DataDescription , Linkage , Module } ;
1212use cranelift_object:: { ObjectBuilder , ObjectModule } ;
1313use subprocess:: { Exec , PopenError } ;
14- const HACKER_DIR : & str = "~/.hacker-lang" ;
14+ use std:: os:: unix:: fs:: PermissionsExt ;
15+ use base64:: engine:: general_purpose:: STANDARD as BASE64_STANDARD ;
16+ use base64:: Engine ;
17+
18+ const HACKER_DIR : & str = "~/.hackeros/hacker-lang" ;
19+
1520fn expand_home ( path : & str ) -> String {
1621 if path. starts_with ( "~/" ) {
17- if let Some ( home) = std :: env:: var_os ( "HOME" ) {
18- return path. replacen ( "~" , home. to_str ( ) . unwrap ( ) , 1 ) ;
22+ if let Some ( home) = env:: var_os ( "HOME" ) {
23+ return path. replacen ( "~" , home. to_str ( ) . unwrap_or ( "" ) , 1 ) ;
1924 }
2025 }
2126 path. to_string ( )
2227}
23- fn parse_hacker_file ( path : & Path , verbose : bool ) -> io:: Result < ( Vec < String > , Vec < String > , Vec < ( String , String ) > , Vec < String > , Vec < String > , Vec < String > ) > {
28+
29+ fn parse_hacker_file ( path : & Path , verbose : bool ) -> io:: Result < ( Vec < String > , Vec < String > , Vec < ( String , String ) > , Vec < String > , Vec < String > , Vec < String > , Vec < String > , std:: collections:: HashMap < String , String > ) > {
2430 let file = File :: open ( path) ?;
2531 let mut deps = Vec :: new ( ) ;
2632 let mut libs = Vec :: new ( ) ;
2733 let mut vars = Vec :: new ( ) ;
2834 let mut cmds = Vec :: new ( ) ;
2935 let mut includes = Vec :: new ( ) ;
36+ let mut binaries = Vec :: new ( ) ; // Binary lib paths
3037 let mut errors = Vec :: new ( ) ;
38+ let mut config = std:: collections:: HashMap :: new ( ) ;
3139 let mut in_config = false ;
32- let mut config_lines = Vec :: new ( ) ;
3340 let mut line_num = 0 ;
3441 for line in io:: BufReader :: new ( file) . lines ( ) {
3542 line_num += 1 ;
@@ -43,7 +50,6 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
4350 errors. push ( format ! ( "Line {}: Nested config section" , line_num) ) ;
4451 }
4552 in_config = true ;
46- config_lines = Vec :: new ( ) ;
4753 continue ;
4854 } else if line == "]" {
4955 if !in_config {
@@ -53,7 +59,11 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
5359 continue ;
5460 }
5561 if in_config {
56- config_lines. push ( line) ;
62+ if let Some ( eq_idx) = line. find ( '=' ) {
63+ let key = line[ ..eq_idx] . trim ( ) . to_string ( ) ;
64+ let value = line[ eq_idx + 1 ..] . trim ( ) . to_string ( ) ;
65+ config. insert ( key, value) ;
66+ }
5767 continue ;
5868 }
5969 if line. starts_with ( "//" ) {
@@ -68,19 +78,24 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
6878 if lib. is_empty ( ) {
6979 errors. push ( format ! ( "Line {}: Empty library/include" , line_num) ) ;
7080 } else {
71- let lib_path_str = expand_home ( & format ! ( "{}/libs/{}/main.hacker" , HACKER_DIR , lib) ) ;
72- let lib_path = Path :: new ( & lib_path_str) ;
73- if lib_path. exists ( ) {
81+ let lib_dir = expand_home ( & format ! ( "{}/libs/{}" , HACKER_DIR , lib) ) ;
82+ let lib_hacker_path = format ! ( "{}/main.hacker" , lib_dir) ;
83+ let lib_bin_path = expand_home ( & format ! ( "{}/libs/{}" , HACKER_DIR , lib) ) ; // Binary file
84+ if Path :: new ( & lib_hacker_path) . exists ( ) {
7485 includes. push ( lib. clone ( ) ) ;
75- let ( sub_deps, sub_libs, sub_vars, sub_cmds, sub_includes, sub_errors) = parse_hacker_file ( lib_path , verbose) ?;
86+ let ( sub_deps, sub_libs, sub_vars, sub_cmds, sub_includes, sub_binaries , sub_errors, sub_config ) = parse_hacker_file ( Path :: new ( & lib_hacker_path ) , verbose) ?;
7687 deps. extend ( sub_deps) ;
7788 libs. extend ( sub_libs) ;
7889 vars. extend ( sub_vars) ;
7990 cmds. extend ( sub_cmds) ;
8091 includes. extend ( sub_includes) ;
92+ binaries. extend ( sub_binaries) ;
8193 for err in sub_errors {
8294 errors. push ( format ! ( "In {}: {}" , lib, err) ) ;
8395 }
96+ config. extend ( sub_config) ;
97+ } else if Path :: new ( & lib_bin_path) . exists ( ) && Path :: new ( & lib_bin_path) . metadata ( ) ?. permissions ( ) . mode ( ) & 0o111 != 0 {
98+ binaries. push ( lib_bin_path) ;
8499 } else {
85100 libs. push ( lib) ;
86101 }
@@ -147,7 +162,7 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
147162 cmds. push ( format ! ( "{} &" , cmd) ) ;
148163 }
149164 } else if line. starts_with ( "!" ) {
150- // Ignore comment
165+ // Comment
151166 } else {
152167 errors. push ( format ! ( "Line {}: Invalid syntax" , line_num) ) ;
153168 }
@@ -161,18 +176,22 @@ fn parse_hacker_file(path: &Path, verbose: bool) -> io::Result<(Vec<String>, Vec
161176 println ! ( "Vars: {:?}" , vars) ;
162177 println ! ( "Cmds: {:?}" , cmds) ;
163178 println ! ( "Includes: {:?}" , includes) ;
179+ println ! ( "Binaries: {:?}" , binaries) ;
180+ println ! ( "Config: {:?}" , config) ;
164181 if !errors. is_empty ( ) {
165182 println ! ( "Errors: {:?}" , errors) ;
166183 }
167184 }
168- Ok ( ( deps, libs, vars, cmds, includes, errors) )
185+ Ok ( ( deps, libs, vars, cmds, includes, binaries , errors, config ) )
169186}
187+
170188fn generate_check_cmd ( dep : & str ) -> String {
171189 if dep == "sudo" {
172190 return String :: new ( ) ;
173191 }
174192 format ! ( "command -v {} &> /dev/null || (sudo apt update && sudo apt install -y {})" , dep, dep)
175193}
194+
176195fn main ( ) -> io:: Result < ( ) > {
177196 let args: Vec < String > = env:: args ( ) . collect ( ) ;
178197 if args. len ( ) < 3 || args. len ( ) > 4 {
@@ -182,7 +201,7 @@ fn main() -> io::Result<()> {
182201 let verbose = args. len ( ) == 4 && args[ 3 ] == "--verbose" ;
183202 let input_path = Path :: new ( & args[ 1 ] ) ;
184203 let output_path = Path :: new ( & args[ 2 ] ) ;
185- let ( deps, _libs, vars, cmds, _includes, errors) = parse_hacker_file ( input_path, verbose) ?;
204+ let ( deps, _libs, vars, cmds, _includes, binaries , errors, _config ) = parse_hacker_file ( input_path, verbose) ?;
186205 if !errors. is_empty ( ) {
187206 for err in errors {
188207 eprintln ! ( "{}" , err) ;
@@ -197,6 +216,19 @@ fn main() -> io::Result<()> {
197216 }
198217 }
199218 final_cmds. extend ( cmds) ;
219+ // For binaries, add commands to extract and run them
220+ let mut bin_extract_cmds = Vec :: new ( ) ;
221+ for ( i, bin_path) in binaries. iter ( ) . enumerate ( ) {
222+ let bin_data = fs:: read ( bin_path) ?;
223+ let data_name = format ! ( "bin_lib_{i}" ) ;
224+ // We'll embed later in Cranelift
225+ // But for cmds, add extraction: e.g., write to /tmp and run
226+ let extract_cmd = format ! ( "echo '{}' | base64 -d > /tmp/{} && chmod +x /tmp/{} && /tmp/{}" , BASE64_STANDARD . encode( & bin_data) , data_name, data_name, data_name) ;
227+ bin_extract_cmds. push ( extract_cmd) ;
228+ }
229+ final_cmds. extend ( bin_extract_cmds) ;
230+
231+ // Cranelift setup
200232 let flag_builder = settings:: builder ( ) ;
201233 let flags = settings:: Flags :: new ( flag_builder) ;
202234 let triple = target_lexicon:: Triple :: host ( ) ;
@@ -205,36 +237,45 @@ fn main() -> io::Result<()> {
205237 let builder = ObjectBuilder :: new (
206238 isa,
207239 output_path. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . as_bytes ( ) . to_vec ( ) ,
208- cranelift_module:: default_libcall_names ( ) ,
209- ) . expect ( "Failed to create ObjectBuilder" ) ;
240+ cranelift_module:: default_libcall_names ( ) ,
241+ ) . expect ( "ObjectBuilder failed " ) ;
210242 let mut module = ObjectModule :: new ( builder) ;
211243 let pointer_type = module. target_config ( ) . pointer_type ( ) ;
244+
245+ // Declare external functions
212246 let mut sig_system = module. make_signature ( ) ;
213247 sig_system. params . push ( AbiParam :: new ( pointer_type) ) ;
214248 sig_system. returns . push ( AbiParam :: new ( types:: I32 ) ) ;
215249 sig_system. call_conv = module. target_config ( ) . default_call_conv ;
216250 let system_id = module. declare_function ( "system" , Linkage :: Import , & sig_system) . unwrap ( ) ;
251+
217252 let mut sig_putenv = module. make_signature ( ) ;
218253 sig_putenv. params . push ( AbiParam :: new ( pointer_type) ) ;
219254 sig_putenv. returns . push ( AbiParam :: new ( types:: I32 ) ) ;
220255 sig_putenv. call_conv = module. target_config ( ) . default_call_conv ;
221256 let putenv_id = module. declare_function ( "putenv" , Linkage :: Import , & sig_putenv) . unwrap ( ) ;
257+
258+ // Main function
222259 let mut sig_main = module. make_signature ( ) ;
223260 sig_main. returns . push ( AbiParam :: new ( types:: I32 ) ) ;
224261 sig_main. call_conv = module. target_config ( ) . default_call_conv ;
225262 let main_id = module. declare_function ( "main" , Linkage :: Export , & sig_main) . unwrap ( ) ;
263+
226264 let mut ctx = cranelift_codegen:: Context :: for_function ( Function :: with_name_signature ( Default :: default ( ) , sig_main. clone ( ) ) ) ;
227265 let mut func_builder_ctx = FunctionBuilderContext :: new ( ) ;
228266 let mut builder = FunctionBuilder :: new ( & mut ctx. func , & mut func_builder_ctx) ;
229267 let entry_block = builder. create_block ( ) ;
230268 builder. switch_to_block ( entry_block) ;
231269 builder. seal_block ( entry_block) ;
270+
232271 let local_system = module. declare_func_in_func ( system_id, & mut builder. func ) ;
233272 let local_putenv = module. declare_func_in_func ( putenv_id, & mut builder. func ) ;
273+
274+ // Embed vars as putenv
234275 let mut var_data_ids = Vec :: new ( ) ;
235- for ( var, value) in & vars {
276+ for ( i , ( var, value) ) in vars. iter ( ) . enumerate ( ) {
236277 let env_str = format ! ( "{}={}" , var, value) ;
237- let data_name = format ! ( "env_{}" , var_data_ids . len ( ) ) ;
278+ let data_name = format ! ( "env_{i}" ) ;
238279 let data_id = module. declare_data ( & data_name, Linkage :: Local , true , false ) . unwrap ( ) ;
239280 let mut data_ctx = DataDescription :: new ( ) ;
240281 let mut bytes: Vec < u8 > = env_str. into_bytes ( ) ;
@@ -246,8 +287,10 @@ fn main() -> io::Result<()> {
246287 for data_id in var_data_ids {
247288 let global = module. declare_data_in_func ( data_id, & mut builder. func ) ;
248289 let ptr = builder. ins ( ) . global_value ( pointer_type, global) ;
249- let _ = builder. ins ( ) . call ( local_putenv, & [ ptr] ) ;
290+ builder. ins ( ) . call ( local_putenv, & [ ptr] ) ;
250291 }
292+
293+ // Embed cmd strings
251294 let mut cmd_data_ids = Vec :: new ( ) ;
252295 for ( i, cmd) in final_cmds. iter ( ) . enumerate ( ) {
253296 let data_name = format ! ( "cmd_{i}" ) ;
@@ -262,19 +305,34 @@ fn main() -> io::Result<()> {
262305 for data_id in cmd_data_ids {
263306 let global = module. declare_data_in_func ( data_id, & mut builder. func ) ;
264307 let ptr = builder. ins ( ) . global_value ( pointer_type, global) ;
265- let _ = builder. ins ( ) . call ( local_system, & [ ptr] ) ;
308+ builder. ins ( ) . call ( local_system, & [ ptr] ) ;
309+ }
310+
311+ // Embed binary libs data
312+ for ( i, bin_path) in binaries. iter ( ) . enumerate ( ) {
313+ let bin_data = fs:: read ( bin_path) ?;
314+ let data_name = format ! ( "bin_lib_data_{i}" ) ;
315+ let data_id = module. declare_data ( & data_name, Linkage :: Local , true , false ) . unwrap ( ) ;
316+ let mut data_ctx = DataDescription :: new ( ) ;
317+ data_ctx. define ( bin_data. into_boxed_slice ( ) ) ;
318+ module. define_data ( data_id, & data_ctx) . unwrap ( ) ;
319+ // In runtime, we would need to extract, but since we're using system calls, we embed extraction cmds above
266320 }
321+
267322 let zero = builder. ins ( ) . iconst ( types:: I32 , 0 ) ;
268323 builder. ins ( ) . return_ ( & [ zero] ) ;
269324 builder. finalize ( ) ;
325+
270326 module. define_function ( main_id, & mut ctx) . unwrap ( ) ;
271- let obj = module. finish ( ) . object . write ( ) . expect ( "Failed to write object" ) ;
327+ let obj = module. finish ( ) . object . write ( ) . expect ( "Write object failed" ) ;
328+
272329 let temp_obj_path = output_path. with_extension ( "o" ) ;
273330 let mut file = File :: create ( & temp_obj_path) ?;
274331 file. write_all ( & obj) ?;
332+
275333 let status = Exec :: shell ( format ! ( "gcc -o {} {}" , output_path. display( ) , temp_obj_path. display( ) ) )
276- . join ( )
277- . map_err ( |e : PopenError | io:: Error :: new ( io:: ErrorKind :: Other , e. to_string ( ) ) ) ?;
334+ . join ( )
335+ . map_err ( |e : PopenError | io:: Error :: new ( io:: ErrorKind :: Other , e. to_string ( ) ) ) ?;
278336 if !status. success ( ) {
279337 return Err ( io:: Error :: new ( io:: ErrorKind :: Other , "Linking failed" ) ) ;
280338 }
0 commit comments