@@ -70,6 +70,7 @@ fn do_ctest() {
7070 t if t. contains ( "windows" ) => test_windows ( t) ,
7171 t if t. contains ( "vxworks" ) => test_vxworks ( t) ,
7272 t if t. contains ( "nto-qnx" ) => test_neutrino ( t) ,
73+ t if t. contains ( "aix" ) => return test_aix ( t) ,
7374 t => panic ! ( "unknown target {t}" ) ,
7475 }
7576}
@@ -102,7 +103,9 @@ fn do_semver() {
102103 // NOTE: Android doesn't include the unix file (or the Linux file) because
103104 // there are some many definitions missing it's actually easier just to
104105 // maintain a file for Android.
105- if family != os && os != "android" {
106+ // NOTE: AIX doesn't include the unix file because there are definitions
107+ // missing on AIX. It is easier to maintain a file for AIX.
108+ if family != os && !matches ! ( os. as_str( ) , "android" | "aix" ) {
106109 process_semver_file ( & mut output, & mut semver_root, & family) ;
107110 }
108111 // We don't do semver for unknown targets.
@@ -5485,3 +5488,238 @@ fn test_haiku(target: &str) {
54855488 } ) ;
54865489 cfg. generate ( src_hotfix_dir ( ) . join ( "lib.rs" ) , "main.rs" ) ;
54875490}
5491+
5492+ fn test_aix ( target : & str ) {
5493+ assert ! ( target. contains( "aix" ) ) ;
5494+
5495+ // ctest generates arguments supported only by clang, so make sure to
5496+ // run with CC=clang. While debugging, "CFLAGS=-ferror-limit=<large num>"
5497+ // is useful to get more error output.
5498+ let mut cfg = ctest_cfg ( ) ;
5499+ cfg. define ( "_THREAD_SAFE" , None ) ;
5500+
5501+ // Avoid the error for definitions such as '{0, 0, 0, 1}' for
5502+ // 'IN6ADDR_LOOPBACK_INIT' in netinent/in.h.
5503+ cfg. flag ( "-Wno-missing-braces" ) ;
5504+
5505+ headers ! { cfg:
5506+ "aio.h" ,
5507+ "ctype.h" ,
5508+ "dirent.h" ,
5509+ "dlfcn.h" ,
5510+ "errno.h" ,
5511+ "fcntl.h" ,
5512+ "fnmatch.h" ,
5513+ "glob.h" ,
5514+ "grp.h" ,
5515+ "iconv.h" ,
5516+ "langinfo.h" ,
5517+ "libgen.h" ,
5518+ "limits.h" ,
5519+ "locale.h" ,
5520+ "malloc.h" ,
5521+ "mntent.h" ,
5522+ "mqueue.h" ,
5523+ "netinet/in.h" , // this needs be before net/if.h
5524+ "poll.h" , // this needs be before net/if.h
5525+ "sys/pollset.h" , // this needs to be before net/if.h
5526+ "net/if.h" ,
5527+ "net/bpf.h" , // this needs to be after net/if.h
5528+ "net/if_dl.h" ,
5529+ "netdb.h" ,
5530+ "netinet/tcp.h" ,
5531+ "pthread.h" ,
5532+ "pwd.h" ,
5533+ "rpcsvc/mount.h" ,
5534+ "rpcsvc/rstat.h" ,
5535+ "regex.h" ,
5536+ "resolv.h" ,
5537+ "sched.h" ,
5538+ "search.h" ,
5539+ "semaphore.h" ,
5540+ "signal.h" ,
5541+ "spawn.h" ,
5542+ "stddef.h" ,
5543+ "stdint.h" ,
5544+ "stdio.h" ,
5545+ "stdlib.h" ,
5546+ "string.h" ,
5547+ "strings.h" ,
5548+ "sys/aacct.h" ,
5549+ "sys/acct.h" ,
5550+ "sys/dr.h" ,
5551+ "sys/file.h" ,
5552+ "sys/io.h" ,
5553+ "sys/ioctl.h" ,
5554+ "sys/ipc.h" ,
5555+ "sys/ldr.h" ,
5556+ "sys/mman.h" ,
5557+ "sys/msg.h" ,
5558+ "sys/reg.h" ,
5559+ "sys/resource.h" ,
5560+ "sys/sem.h" ,
5561+ "sys/shm.h" ,
5562+ "sys/socket.h" ,
5563+ "sys/stat.h" ,
5564+ "sys/statfs.h" ,
5565+ "sys/statvfs.h" ,
5566+ "sys/stropts.h" ,
5567+ "sys/termio.h" ,
5568+ "sys/time.h" ,
5569+ "sys/times.h" ,
5570+ "sys/types.h" ,
5571+ "sys/uio.h" ,
5572+ "sys/un.h" ,
5573+ "sys/user.h" ,
5574+ "sys/utsname.h" ,
5575+ "sys/vattr.h" ,
5576+ "sys/vminfo.h" ,
5577+ "sys/wait.h" ,
5578+ "sys/xti.h" ,
5579+ "syslog.h" ,
5580+ "termios.h" ,
5581+ "thread.h" ,
5582+ "time.h" ,
5583+ "ucontext.h" ,
5584+ "unistd.h" ,
5585+ "utime.h" ,
5586+ "utmp.h" ,
5587+ "utmpx.h" ,
5588+ "wchar.h" ,
5589+ }
5590+
5591+ cfg. skip_type ( move |ty| match ty {
5592+ // AIX does not define type 'sighandler_t'.
5593+ "sighandler_t" => true ,
5594+
5595+ // The alignment of 'double' does not agree between C and Rust for AIX.
5596+ // We are working on a resolution.
5597+ "c_double" => true ,
5598+
5599+ _ => false ,
5600+ } ) ;
5601+
5602+ cfg. type_name ( move |ty, is_struct, is_union| match ty {
5603+ "DIR" => ty. to_string ( ) ,
5604+ "FILE" => ty. to_string ( ) ,
5605+ "ACTION" => ty. to_string ( ) ,
5606+
5607+ // 'sigval' is a struct in Rust, but a union in C.
5608+ "sigval" => format ! ( "union sigval" ) ,
5609+
5610+ t if t. ends_with ( "_t" ) => t. to_string ( ) ,
5611+ t if is_struct => format ! ( "struct {}" , t) ,
5612+ t if is_union => format ! ( "union {}" , t) ,
5613+ t => t. to_string ( ) ,
5614+ } ) ;
5615+
5616+ cfg. skip_const ( move |name| match name {
5617+ // Skip 'sighandler_t' assignments.
5618+ "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true ,
5619+
5620+ _ => false ,
5621+ } ) ;
5622+
5623+ cfg. skip_struct ( move |ty| {
5624+ match ty {
5625+ // FIXME(union): actually a union.
5626+ "sigval" => true ,
5627+
5628+ // '__poll_ctl_ext_u' and '__pollfd_ext_u' are for unnamed unions.
5629+ "__poll_ctl_ext_u" => true ,
5630+ "__pollfd_ext_u" => true ,
5631+
5632+ // 'struct fpreg_t' is not defined in AIX headers. It is created to
5633+ // allow type 'double' to be used in signal contexts.
5634+ "fpreg_t" => true ,
5635+
5636+ _ => false ,
5637+ }
5638+ } ) ;
5639+
5640+ cfg. skip_field_type ( move |struct_, field| {
5641+ match ( struct_, field) {
5642+ // AIX does not define 'sighandler_t'.
5643+ ( "sigaction" , "sa_sigaction" ) => true ,
5644+
5645+ // The type of 'fpr' is 'fpreg_t' which is created to allow type
5646+ // 'double' to be used in signal contexts.
5647+ ( "__context64" , "fpr" ) => true ,
5648+ ( "__tm_context_t" , "fpr" ) => true ,
5649+
5650+ _ => false ,
5651+ }
5652+ } ) ;
5653+
5654+ cfg. skip_field ( move |s, field| {
5655+ match s {
5656+ // The field 'u' is actually a unnamed union in the AIX header.
5657+ "poll_ctl_ext" if field == "u" => true ,
5658+
5659+ // The field 'data' is actually a unnamed union in the AIX header.
5660+ "pollfd_ext" if field == "data" => true ,
5661+
5662+ _ => false ,
5663+ }
5664+ } ) ;
5665+
5666+ cfg. skip_fn ( move |name| {
5667+ match name {
5668+ // 'sighandler_t' is not defined on AIX.
5669+ "signal" => true ,
5670+
5671+ // The function is only available under macro _USE_IRS in 'netdb.h'.
5672+ "hstrerror" => true ,
5673+
5674+ // _ALL_SOURCE signatures for these functions differ from POSIX's
5675+ // on AIX.
5676+ "poll" => true ,
5677+ "readlinkat" => true ,
5678+ "readlink" => true ,
5679+ "pselect" => true ,
5680+
5681+ // The AIX signature differs from POSIX's, issue opened.
5682+ "gai_strerror" => true ,
5683+
5684+ // AIX implements POSIX-compliant versions of these functions
5685+ // using 'static' wrappers in the headers, which in turn call
5686+ // the corresponding system libc functions prefixed with '_posix_'
5687+ // (e.g., '_posix_aio_read' for 'aio_read').
5688+ // On the Rust side, these functions resolve directly to the
5689+ // POSIX-compliant versions in the system libc. As a result,
5690+ // function pointer comparisons between the C and Rust sides
5691+ // would fail.
5692+ "getpwuid_r" | "getpwnam_r" | "getgrgid_r" | "getgrnam_r"
5693+ | "aio_cancel" | "aio_error" | "aio_fsync" | "aio_read"
5694+ | "aio_return" | "aio_suspend" | "aio_write" | "select" => true ,
5695+
5696+ // 'getdtablesize' is a constant in the AIX header but it is
5697+ // a real function in libc which the Rust side is resolved to.
5698+ // The function pointer comparison test would fail.
5699+ "getdtablesize" => true ,
5700+
5701+ // FIXME(ctest): Our API is unsound. The Rust API allows aliasing
5702+ // pointers, but the C API requires pointers not to alias.
5703+ // We should probably be at least using '&'/'&mut' here, see:
5704+ // https://github.com/gnzlbg/ctest/issues/68.
5705+ "lio_listio" => true ,
5706+
5707+ _ => false ,
5708+ }
5709+ } ) ;
5710+
5711+
5712+ cfg. volatile_item ( |i| {
5713+ use ctest:: VolatileItemKind :: * ;
5714+ match i {
5715+ // 'aio_buf' is of type 'volatile void**' but since we cannot
5716+ // express that in Rust types, we have to explicitly tell the
5717+ // checker about it here.
5718+ StructField ( ref n, ref f) if n == "aiocb" && f == "aio_buf" => true ,
5719+
5720+ _ => false ,
5721+ }
5722+ } ) ;
5723+
5724+ cfg. generate ( src_hotfix_dir ( ) . join ( "lib.rs" ) , "main.rs" ) ;
5725+ }
0 commit comments