@@ -125,7 +125,15 @@ pub fn cargo(args: &[OsString], verb: &str) -> Result<Option<i32>> {
125125 Ok ( exit_status. code ( ) )
126126}
127127
128- /// This is comparable to ament_index_register_resource() in CMake
128+ /// Create an ament resource index marker file for a package
129+ ///
130+ /// This function registers a package to ament by creating an empty marker file at
131+ /// `share/ament_index/resource_index` with the package name as filename.
132+ ///
133+ /// The presence of this file is used by ament and colcon to discover installed packages and other resources.
134+ /// For more information:
135+ /// - Design doc: https://github.com/ament/ament_cmake/blob/2366f15479e37d552d4e225f09ccef1c6ccc8c4e/ament_cmake_core/doc/resource_index.md
136+ /// - Reference implementation of CMake: https://github.com/ament/ament_cmake/blob/2366f15479e37d552d4e225f09ccef1c6ccc8c4e/ament_cmake_core/cmake/index/ament_index_register_resource.cmake
129137pub fn create_package_marker (
130138 install_base : impl AsRef < Path > ,
131139 marker_dir : & str ,
@@ -150,7 +158,7 @@ pub fn create_package_marker(
150158 Ok ( ( ) )
151159}
152160
153- /// Copies files or directories.
161+ /// Copies files or directories recursively .
154162fn copy ( src : impl AsRef < Path > , dest_dir : impl AsRef < Path > ) -> Result < ( ) > {
155163 let src = src. as_ref ( ) ;
156164 let dest = dest_dir. as_ref ( ) . join ( src. file_name ( ) . unwrap ( ) ) ;
@@ -349,3 +357,204 @@ pub fn install_files_from_metadata(
349357 }
350358 Ok ( ( ) )
351359}
360+
361+ #[ cfg( test) ]
362+ mod tests {
363+ use super :: * ;
364+ use std:: collections:: HashMap ;
365+ use std:: fs:: File ;
366+ use std:: io:: Write ;
367+ use tempfile:: tempdir;
368+
369+ #[ test]
370+ fn test_create_package_marker ( ) -> Result < ( ) > {
371+ let tmp = tempdir ( ) ?;
372+ let install_base = tmp. path ( ) ;
373+
374+ create_package_marker ( install_base, "packages" , "test_package" ) ?;
375+
376+ let marker_path =
377+ install_base. join ( "share/ament_index/resource_index/packages/test_package" ) ;
378+
379+ assert ! ( marker_path. exists( ) ) ;
380+ assert ! ( marker_path. is_file( ) ) ;
381+ Ok ( ( ) )
382+ }
383+
384+ #[ test]
385+ fn test_copy_recursive ( ) -> Result < ( ) > {
386+ let tmp = tempdir ( ) ?;
387+ let src_dir = tmp. path ( ) . join ( "src_folder" ) ;
388+ let dest_dir = tmp. path ( ) . join ( "dest_folder" ) ;
389+
390+ std:: fs:: create_dir_all ( src_dir. join ( "sub" ) ) ?;
391+ File :: create ( src_dir. join ( "file.txt" ) ) ?. write_all ( b"hello" ) ?;
392+ File :: create ( src_dir. join ( "sub/inner.txt" ) ) ?. write_all ( b"world" ) ?;
393+
394+ std:: fs:: create_dir_all ( & dest_dir) ?;
395+
396+ copy ( & src_dir, & dest_dir) ?;
397+
398+ assert ! ( dest_dir. join( "src_folder/file.txt" ) . exists( ) ) ;
399+ assert ! ( dest_dir. join( "src_folder/sub/inner.txt" ) . exists( ) ) ;
400+ Ok ( ( ) )
401+ }
402+
403+ #[ test]
404+ fn test_install_binaries_feature_filtering ( ) -> Result < ( ) > {
405+ let tmp = tempdir ( ) ?;
406+ let build_base = tmp. path ( ) . join ( "target" ) ;
407+ let install_base = tmp. path ( ) . join ( "install" ) ;
408+ let profile = "debug" ;
409+
410+ // Create dummy binaries in build dir. One of them requires a feature that wasn't used during
411+ // compilation and should therefore not be installed
412+ let bin_dir = build_base. join ( profile) ;
413+ std:: fs:: create_dir_all ( & bin_dir) ?;
414+ File :: create ( bin_dir. join ( "my_bin" ) ) ?;
415+ File :: create ( bin_dir. join ( "skipped_bin" ) ) ?;
416+
417+ let mut features = HashSet :: new ( ) ;
418+ features. insert ( "required_feat" . to_string ( ) ) ;
419+
420+ let binaries = vec ! [
421+ Product {
422+ name: Some ( "my_bin" . to_string( ) ) ,
423+ required_features: vec![ "required_feat" . to_string( ) ] ,
424+ ..Default :: default ( )
425+ } ,
426+ Product {
427+ name: Some ( "skipped_bin" . to_string( ) ) ,
428+ required_features: vec![ "missing_feat" . to_string( ) ] ,
429+ ..Default :: default ( )
430+ } ,
431+ ] ;
432+
433+ install_binaries (
434+ & install_base,
435+ & build_base,
436+ "my_package" ,
437+ profile,
438+ None ,
439+ & features,
440+ & binaries,
441+ ) ?;
442+
443+ assert ! ( install_base. join( "lib/my_package/my_bin" ) . exists( ) ) ;
444+ assert ! ( !install_base. join( "lib/my_package/skipped_bin" ) . exists( ) ) ;
445+
446+ Ok ( ( ) )
447+ }
448+
449+ #[ test]
450+ fn test_install_binaries_with_arch ( ) -> Result < ( ) > {
451+ let tmp = tempdir ( ) ?;
452+ let build_base = tmp. path ( ) . join ( "target" ) ;
453+ let install_base = tmp. path ( ) . join ( "install" ) ;
454+ let package_name = "arch_test" ;
455+ let profile = "debug" ;
456+ let arch = "x86_64-unknown-linux-gnu" ;
457+
458+ let src_dir_x86 = build_base. join ( arch) . join ( profile) ;
459+ let src_dir_aarch = build_base. join ( "aarch64-unknown-linux-gnu" ) . join ( profile) ;
460+ std:: fs:: create_dir_all ( & src_dir_x86) ?;
461+ std:: fs:: create_dir_all ( & src_dir_aarch) ?;
462+
463+ std:: fs:: write ( src_dir_x86. join ( "libarch_test.so" ) , "x86" ) ?;
464+ std:: fs:: write ( src_dir_aarch. join ( "libarch_test.so" ) , "aarch" ) ?;
465+
466+ install_binaries (
467+ & install_base,
468+ & build_base,
469+ package_name,
470+ profile,
471+ Some ( arch) ,
472+ & HashSet :: new ( ) ,
473+ & [ ] ,
474+ ) ?;
475+
476+ let dest_file = install_base
477+ . join ( "lib" )
478+ . join ( package_name)
479+ . join ( "libarch_test.so" ) ;
480+
481+ assert ! ( dest_file. exists( ) ) ;
482+
483+ let installed_content = std:: fs:: read_to_string ( dest_file) ?;
484+ assert_eq ! ( installed_content, "x86" ) ;
485+
486+ Ok ( ( ) )
487+ }
488+
489+ #[ test]
490+ fn test_install_binaries_lib_variants ( ) -> Result < ( ) > {
491+ let tmp = tempdir ( ) ?;
492+ let build_base = tmp. path ( ) . join ( "target" ) ;
493+ let install_base = tmp. path ( ) . join ( "install" ) ;
494+ let package_name = "my_rust_lib" ;
495+ let profile = "release" ;
496+
497+ let src_dir = build_base. join ( profile) ;
498+ std:: fs:: create_dir_all ( & src_dir) ?;
499+
500+ let so_path = src_dir. join ( "libmy_rust_lib.so" ) ;
501+ let a_path = src_dir. join ( "libmy_rust_lib.a" ) ;
502+ let dll_path = src_dir. join ( "my_rust_lib.dll" ) ;
503+ let lib_path = src_dir. join ( "my_rust_lib.lib" ) ;
504+ let dylib_path = src_dir. join ( "libmy_rust_lib.dylib" ) ;
505+
506+ File :: create ( & so_path) ?;
507+ File :: create ( & a_path) ?;
508+ File :: create ( & dll_path) ?;
509+ File :: create ( & lib_path) ?;
510+ File :: create ( & dylib_path) ?;
511+
512+ install_binaries (
513+ & install_base,
514+ & build_base,
515+ package_name,
516+ profile,
517+ None ,
518+ & HashSet :: new ( ) ,
519+ & [ ] ,
520+ ) ?;
521+
522+ let dest_dir = install_base. join ( "lib" ) . join ( package_name) ;
523+
524+ assert ! ( dest_dir. join( "libmy_rust_lib.so" ) . exists( ) ) ;
525+ assert ! ( dest_dir. join( "libmy_rust_lib.a" ) . exists( ) ) ;
526+ assert ! ( dest_dir. join( "my_rust_lib.dll" ) . exists( ) ) ;
527+ assert ! ( dest_dir. join( "my_rust_lib.lib" ) . exists( ) ) ;
528+ assert ! ( dest_dir. join( "libmy_rust_lib.dylib" ) . exists( ) ) ;
529+
530+ Ok ( ( ) )
531+ }
532+
533+ #[ test]
534+ fn test_install_files_from_metadata ( ) -> Result < ( ) > {
535+ let tmp = tempdir ( ) ?;
536+ let package_path = tmp. path ( ) . join ( "pkg" ) ;
537+ let install_base = tmp. path ( ) . join ( "install" ) ;
538+
539+ std:: fs:: create_dir_all ( package_path. join ( "launch" ) ) ?;
540+ File :: create ( package_path. join ( "launch/robot.py" ) ) ?;
541+
542+ /* Create serialized metadata akin to:
543+ ```toml
544+ [package.metadata.ros]
545+ install_to_share = ["launch"]
546+ ```
547+ */
548+ let mut metadata_table_entries: HashMap < String , Value > = HashMap :: new ( ) ;
549+ metadata_table_entries. insert (
550+ String :: from ( "ros" ) ,
551+ Value :: from ( HashMap :: from ( [ ( "install_to_share" , vec ! [ "launch" ] ) ] ) ) ,
552+ ) ;
553+ let metadata_table = cargo_manifest:: Value :: from ( metadata_table_entries) ;
554+
555+ install_files_from_metadata ( & install_base, & package_path, "pkg" , Some ( & metadata_table) ) ?;
556+
557+ assert ! ( install_base. join( "share/pkg/launch/robot.py" ) . exists( ) ) ;
558+ Ok ( ( ) )
559+ }
560+ }
0 commit comments