@@ -68,6 +68,7 @@ fn do_ctest() {
6868 t if t. contains ( "windows" ) => test_windows ( t) ,
6969 t if t. contains ( "vxworks" ) => test_vxworks ( t) ,
7070 t if t. contains ( "nto-qnx" ) => test_neutrino ( t) ,
71+ t if t. contains ( "aix" ) => return test_aix ( t) ,
7172 t => panic ! ( "unknown target {t}" ) ,
7273 }
7374}
@@ -95,7 +96,9 @@ fn do_semver() {
9596 // NOTE: Android doesn't include the unix file (or the Linux file) because
9697 // there are some many definitions missing it's actually easier just to
9798 // maintain a file for Android.
98- if family != os && os != "android" {
99+ // NOTE: AIX doesn't include the unix file because there are definitions
100+ // missing on AIX. It is easier to maintain a file for AIX.
101+ if family != os && !matches ! ( os. as_str( ) , "android" | "aix" ) {
99102 process_semver_file ( & mut output, & mut semver_root, & family) ;
100103 }
101104 // We don't do semver for unknown targets.
@@ -5408,3 +5411,238 @@ fn test_haiku(target: &str) {
54085411 } ) ;
54095412 cfg. generate ( src_hotfix_dir ( ) . join ( "lib.rs" ) , "main.rs" ) ;
54105413}
5414+
5415+ fn test_aix ( target : & str ) {
5416+ assert ! ( target. contains( "aix" ) ) ;
5417+
5418+ // ctest generates arguments supported only by clang, so make sure to
5419+ // run with CC=clang. While debugging, "CFLAGS=-ferror-limit=<large num>"
5420+ // is useful to get more error output.
5421+ let mut cfg = ctest_cfg ( ) ;
5422+ cfg. define ( "_THREAD_SAFE" , None ) ;
5423+
5424+ // Avoid the error for definitions such as '{0, 0, 0, 1}' for
5425+ // 'IN6ADDR_LOOPBACK_INIT' in netinent/in.h.
5426+ cfg. flag ( "-Wno-missing-braces" ) ;
5427+
5428+ headers ! { cfg:
5429+ "aio.h" ,
5430+ "ctype.h" ,
5431+ "dirent.h" ,
5432+ "dlfcn.h" ,
5433+ "errno.h" ,
5434+ "fcntl.h" ,
5435+ "fnmatch.h" ,
5436+ "glob.h" ,
5437+ "grp.h" ,
5438+ "iconv.h" ,
5439+ "langinfo.h" ,
5440+ "libgen.h" ,
5441+ "limits.h" ,
5442+ "locale.h" ,
5443+ "malloc.h" ,
5444+ "mntent.h" ,
5445+ "mqueue.h" ,
5446+ "netinet/in.h" , // this needs be before net/if.h
5447+ "poll.h" , // this needs be before net/if.h
5448+ "sys/pollset.h" , // this needs to be before net/if.h
5449+ "net/if.h" ,
5450+ "net/bpf.h" , // this needs to be after net/if.h
5451+ "net/if_dl.h" ,
5452+ "netdb.h" ,
5453+ "netinet/tcp.h" ,
5454+ "pthread.h" ,
5455+ "pwd.h" ,
5456+ "rpcsvc/mount.h" ,
5457+ "rpcsvc/rstat.h" ,
5458+ "regex.h" ,
5459+ "resolv.h" ,
5460+ "sched.h" ,
5461+ "search.h" ,
5462+ "semaphore.h" ,
5463+ "signal.h" ,
5464+ "spawn.h" ,
5465+ "stddef.h" ,
5466+ "stdint.h" ,
5467+ "stdio.h" ,
5468+ "stdlib.h" ,
5469+ "string.h" ,
5470+ "strings.h" ,
5471+ "sys/aacct.h" ,
5472+ "sys/acct.h" ,
5473+ "sys/dr.h" ,
5474+ "sys/file.h" ,
5475+ "sys/io.h" ,
5476+ "sys/ioctl.h" ,
5477+ "sys/ipc.h" ,
5478+ "sys/ldr.h" ,
5479+ "sys/mman.h" ,
5480+ "sys/msg.h" ,
5481+ "sys/reg.h" ,
5482+ "sys/resource.h" ,
5483+ "sys/sem.h" ,
5484+ "sys/shm.h" ,
5485+ "sys/socket.h" ,
5486+ "sys/stat.h" ,
5487+ "sys/statfs.h" ,
5488+ "sys/statvfs.h" ,
5489+ "sys/stropts.h" ,
5490+ "sys/termio.h" ,
5491+ "sys/time.h" ,
5492+ "sys/times.h" ,
5493+ "sys/types.h" ,
5494+ "sys/uio.h" ,
5495+ "sys/un.h" ,
5496+ "sys/user.h" ,
5497+ "sys/utsname.h" ,
5498+ "sys/vattr.h" ,
5499+ "sys/vminfo.h" ,
5500+ "sys/wait.h" ,
5501+ "sys/xti.h" ,
5502+ "syslog.h" ,
5503+ "termios.h" ,
5504+ "thread.h" ,
5505+ "time.h" ,
5506+ "ucontext.h" ,
5507+ "unistd.h" ,
5508+ "utime.h" ,
5509+ "utmp.h" ,
5510+ "utmpx.h" ,
5511+ "wchar.h" ,
5512+ }
5513+
5514+ cfg. skip_type ( move |ty| match ty {
5515+ // AIX does not define type 'sighandler_t'.
5516+ "sighandler_t" => true ,
5517+
5518+ // The alignment of 'double' does not agree between C and Rust for AIX.
5519+ // We are working on a resolution.
5520+ "c_double" => true ,
5521+
5522+ _ => false ,
5523+ } ) ;
5524+
5525+ cfg. type_name ( move |ty, is_struct, is_union| match ty {
5526+ "DIR" => ty. to_string ( ) ,
5527+ "FILE" => ty. to_string ( ) ,
5528+ "ACTION" => ty. to_string ( ) ,
5529+
5530+ // 'sigval' is a struct in Rust, but a union in C.
5531+ "sigval" => format ! ( "union sigval" ) ,
5532+
5533+ t if t. ends_with ( "_t" ) => t. to_string ( ) ,
5534+ t if is_struct => format ! ( "struct {}" , t) ,
5535+ t if is_union => format ! ( "union {}" , t) ,
5536+ t => t. to_string ( ) ,
5537+ } ) ;
5538+
5539+ cfg. skip_const ( move |name| match name {
5540+ // Skip 'sighandler_t' assignments.
5541+ "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true ,
5542+
5543+ _ => false ,
5544+ } ) ;
5545+
5546+ cfg. skip_struct ( move |ty| {
5547+ match ty {
5548+ // FIXME(union): actually a union.
5549+ "sigval" => true ,
5550+
5551+ // '__poll_ctl_ext_u' and '__pollfd_ext_u' are for unnamed unions.
5552+ "__poll_ctl_ext_u" => true ,
5553+ "__pollfd_ext_u" => true ,
5554+
5555+ // 'struct fpreg_t' is not defined in AIX headers. It is created to
5556+ // allow type 'double' to be used in signal contexts.
5557+ "fpreg_t" => true ,
5558+
5559+ _ => false ,
5560+ }
5561+ } ) ;
5562+
5563+ cfg. skip_field_type ( move |struct_, field| {
5564+ match ( struct_, field) {
5565+ // AIX does not define 'sighandler_t'.
5566+ ( "sigaction" , "sa_sigaction" ) => true ,
5567+
5568+ // The type of 'fpr' is 'fpreg_t' which is created to allow type
5569+ // 'double' to be used in signal contexts.
5570+ ( "__context64" , "fpr" ) => true ,
5571+ ( "__tm_context_t" , "fpr" ) => true ,
5572+
5573+ _ => false ,
5574+ }
5575+ } ) ;
5576+
5577+ cfg. skip_field ( move |s, field| {
5578+ match s {
5579+ // The field 'u' is actually a unnamed union in the AIX header.
5580+ "poll_ctl_ext" if field == "u" => true ,
5581+
5582+ // The field 'data' is actually a unnamed union in the AIX header.
5583+ "pollfd_ext" if field == "data" => true ,
5584+
5585+ _ => false ,
5586+ }
5587+ } ) ;
5588+
5589+ cfg. skip_fn ( move |name| {
5590+ match name {
5591+ // 'sighandler_t' is not defined on AIX.
5592+ "signal" => true ,
5593+
5594+ // The function is only available under macro _USE_IRS in 'netdb.h'.
5595+ "hstrerror" => true ,
5596+
5597+ // _ALL_SOURCE signatures for these functions differ from POSIX's
5598+ // on AIX.
5599+ "poll" => true ,
5600+ "readlinkat" => true ,
5601+ "readlink" => true ,
5602+ "pselect" => true ,
5603+
5604+ // The AIX signature differs from POSIX's, issue opened.
5605+ "gai_strerror" => true ,
5606+
5607+ // AIX implements POSIX-compliant versions of these functions
5608+ // using 'static' wrappers in the headers, which in turn call
5609+ // the corresponding system libc functions prefixed with '_posix_'
5610+ // (e.g., '_posix_aio_read' for 'aio_read').
5611+ // On the Rust side, these functions resolve directly to the
5612+ // POSIX-compliant versions in the system libc. As a result,
5613+ // function pointer comparisons between the C and Rust sides
5614+ // would fail.
5615+ "getpwuid_r" | "getpwnam_r" | "getgrgid_r" | "getgrnam_r"
5616+ | "aio_cancel" | "aio_error" | "aio_fsync" | "aio_read"
5617+ | "aio_return" | "aio_suspend" | "aio_write" | "select" => true ,
5618+
5619+ // 'getdtablesize' is a constant in the AIX header but it is
5620+ // a real function in libc which the Rust side is resolved to.
5621+ // The function pointer comparison test would fail.
5622+ "getdtablesize" => true ,
5623+
5624+ // FIXME(ctest): Our API is unsound. The Rust API allows aliasing
5625+ // pointers, but the C API requires pointers not to alias.
5626+ // We should probably be at least using '&'/'&mut' here, see:
5627+ // https://github.com/gnzlbg/ctest/issues/68.
5628+ "lio_listio" => true ,
5629+
5630+ _ => false ,
5631+ }
5632+ } ) ;
5633+
5634+
5635+ cfg. volatile_item ( |i| {
5636+ use ctest:: VolatileItemKind :: * ;
5637+ match i {
5638+ // 'aio_buf' is of type 'volatile void**' but since we cannot
5639+ // express that in Rust types, we have to explicitly tell the
5640+ // checker about it here.
5641+ StructField ( ref n, ref f) if n == "aiocb" && f == "aio_buf" => true ,
5642+
5643+ _ => false ,
5644+ }
5645+ } ) ;
5646+
5647+ cfg. generate ( src_hotfix_dir ( ) . join ( "lib.rs" ) , "main.rs" ) ;
5648+ }
0 commit comments