Enable GN to build & run on z/OS All changes are guarded in source files by OS_ZOS macro (defined ifdef __MVS__), and in build/gen.py by platform.is_zos(). Summary of changes: - build: add ninja template file - gen: updates to generate the ninja build files, and add dependency on zoslib (https://github.com/ibmruntimes/zoslib) to use its APIs - config: define OS_ZOS and OS_POSIX - src: include OS host "zos" in SetSystemVarsLocked() - src: use APIs from zoslib for thread_local impl'n, GetExePath(), sem_init/post/wait/destroy(), and a basic version of clock_gettime() - src: workaround a Woz compiler bug (arg to std::pair) - src: use utime, stat, fd_set, exclude headers - src: use stat, mode, mktemp - src: change to not assume O_RDONLY is 0 on all platforms (2 on z/OS) Note: files generated by GN must be manually tagged, so they can be read: $ find . -type f -name "*.ninja*" -exec chtag -tc819 {} \; Change-Id: Id06e12c9ca88424a9495a5a2d1ed4e2575185196 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/11520 Reviewed-by: Brett Wilson <brettw@chromium.org> Commit-Queue: Brett Wilson <brettw@chromium.org>
diff --git a/README.md b/README.md index f62e28f..2f5d232 100644 --- a/README.md +++ b/README.md
@@ -101,10 +101,20 @@ in `PATH`, so you'll want to run from a Visual Studio command prompt, or similar. -On Linux and Mac, the default compiler is `clang++`, a recent version is +On Linux, Mac and z/OS, the default compiler is `clang++`, a recent version is expected to be found in `PATH`. This can be overridden by setting `CC`, `CXX`, and `AR`. +On z/OS, building GN requires [ZOSLIB](https://github.com/ibmruntimes/zoslib) to be +installed, as described at that URL. When building with `build/gen.py`, use the option +`--zoslib-dir` to specify the path to [ZOSLIB](https://github.com/ibmruntimes/zoslib): + + cd gn + python build/gen.py --zoslib-dir /path/to/zoslib + +By default, if you don't specify `--zoslib-dir`, `gn/build/gen.py` expects to find +`zoslib` directory under `gn/third_party/`. + ## Examples There is a simple example in [examples/simple_build](examples/simple_build)
diff --git a/build/build_zos.ninja.template b/build/build_zos.ninja.template new file mode 100644 index 0000000..f9f0c96 --- /dev/null +++ b/build/build_zos.ninja.template
@@ -0,0 +1,13 @@ +rule cxx + command = $cxx $includes $cflags -c $in -o $out + description = CXX $out + depfile = $out.d + deps = gcc + +rule alink_thin + command = rm -f $out && $ar rcsT $out $in + description = AR $out + +rule link + command = $ld $ldflags -o $out $in $libs $solibs + description = LINK $out
diff --git a/build/gen.py b/build/gen.py index 922bf46..1f23cc4 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -23,7 +23,6 @@ SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) REPO_ROOT = os.path.dirname(SCRIPT_DIR) - class Platform(object): """Represents a host/target platform.""" def __init__(self, platform): @@ -55,10 +54,12 @@ self._platform = 'haiku' elif self._platform.startswith('sunos'): self._platform = 'solaris' + elif self._platform.startswith('zos'): + self._platform = 'zos' @staticmethod def known_platforms(): - return ['linux', 'darwin', 'mingw', 'msys', 'msvc', 'aix', 'fuchsia', 'freebsd', 'netbsd', 'openbsd', 'haiku', 'solaris'] + return ['linux', 'darwin', 'mingw', 'msys', 'msvc', 'aix', 'fuchsia', 'freebsd', 'netbsd', 'openbsd', 'haiku', 'solaris', 'zos'] def platform(self): return self._platform @@ -93,6 +94,9 @@ def is_posix(self): return self._platform in ['linux', 'freebsd', 'darwin', 'aix', 'openbsd', 'haiku', 'solaris', 'msys', 'netbsd'] + def is_zos(self): + return self._platform == 'zos' + def main(argv): parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) @@ -129,6 +133,16 @@ 'library, or \'-l<name>\' on POSIX systems. Can be ' + 'used multiple times. Useful to link custom malloc ' + 'or cpu profiling libraries.')) + if sys.platform == 'zos': + parser.add_option('--zoslib-dir', + action='store', + default='../third_party/zoslib', + dest='zoslib_dir', + help=('Specify the path of ZOSLIB directory, to link ' + + 'with <ZOSLIB_DIR>/install/lib/libzoslib.a, and ' + + 'add -I<ZOSLIB_DIR>/install/include to the compile ' + + 'commands. See README.md for details.')) + options, args = parser.parse_args(argv) if args: @@ -218,6 +232,7 @@ 'haiku': 'build_haiku.ninja.template', 'solaris': 'build_linux.ninja.template', 'netbsd': 'build_linux.ninja.template', + 'zos': 'build_zos.ninja.template', }[platform.platform()]) with open(template_filename) as f: @@ -319,6 +334,9 @@ os.path.relpath(os.path.join(REPO_ROOT, 'src'), os.path.dirname(path)), '.', ] + if platform.is_zos(): + include_dirs += [ options.zoslib_dir + '/install/include' ] + libs = [] if not platform.is_msvc(): @@ -337,8 +355,8 @@ ldflags.extend(['-fdata-sections', '-ffunction-sections']) if platform.is_darwin(): ldflags.append('-Wl,-dead_strip') - elif not platform.is_aix() and not platform.is_solaris(): - # Garbage collection is done by default on aix. + elif not platform.is_aix() and not platform.is_solaris() and not platform.is_zos(): + # Garbage collection is done by default on aix, and option is unsupported on z/OS. ldflags.append('-Wl,--gc-sections') # Omit all symbol information from the output file. @@ -349,7 +367,8 @@ ldflags.append('-Wl,-s') elif platform.is_solaris(): ldflags.append('-Wl,--strip-all') - else: + elif not platform.is_zos(): + # /bin/ld on z/OS doesn't have an equivalent option. ldflags.append('-Wl,-strip-all') # Enable identical code-folding. @@ -405,6 +424,11 @@ elif platform.is_haiku(): cflags.append('-fPIC') cflags.extend(['-D_BSD_SOURCE']) + elif platform.is_zos(): + cflags.append('-fzos-le-char-mode=ascii') + cflags.append('-Wno-unused-function') + cflags.append('-D_OPEN_SYS_FILE_EXT') + cflags.append('-DPATH_MAX=1024') if platform.is_posix() and not platform.is_haiku(): ldflags.append('-pthread') @@ -745,7 +769,7 @@ ], 'libs': []}, } - if platform.is_posix(): + if platform.is_posix() or platform.is_zos(): static_libraries['base']['sources'].extend([ 'src/base/files/file_enumerator_posix.cc', 'src/base/files/file_posix.cc', @@ -754,6 +778,9 @@ 'src/base/posix/safe_strerror.cc', ]) + if platform.is_zos(): + libs.extend([ options.zoslib_dir + '/install/lib/libzoslib.a' ]) + if platform.is_windows(): static_libraries['base']['sources'].extend([ 'src/base/files/file_enumerator_win.cc',
diff --git a/src/base/files/file.h b/src/base/files/file.h index b286f69..737e30a 100644 --- a/src/base/files/file.h +++ b/src/base/files/file.h
@@ -23,7 +23,8 @@ namespace base { #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ - defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ANDROID) && __ANDROID_API__ < 21 + defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ZOS) || \ + defined(OS_ANDROID) && __ANDROID_API__ < 21 typedef struct stat stat_wrapper_t; #elif defined(OS_POSIX) || defined(OS_FUCHSIA) typedef struct stat64 stat_wrapper_t;
diff --git a/src/base/files/file_posix.cc b/src/base/files/file_posix.cc index 349cbfe..b1f9f5e 100644 --- a/src/base/files/file_posix.cc +++ b/src/base/files/file_posix.cc
@@ -25,7 +25,8 @@ namespace { #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ - defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ANDROID) && __ANDROID_API__ < 21 + defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ZOS) || \ + defined(OS_ANDROID) && __ANDROID_API__ < 21 int CallFstat(int fd, stat_wrapper_t* sb) { return fstat(fd, sb); } @@ -93,7 +94,7 @@ int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec; time_t creation_time_sec = stat_info.st_ctimespec.tv_sec; int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec; -#elif defined(OS_AIX) +#elif defined(OS_AIX) || defined(OS_ZOS) time_t last_modified_sec = stat_info.st_mtime; int64_t last_modified_nsec = 0; time_t last_accessed_sec = stat_info.st_atime; @@ -329,7 +330,6 @@ DCHECK(!IsValid()); int open_flags = 0; - created_ = false; if (flags & FLAG_CREATE_ALWAYS) { DCHECK(!open_flags); @@ -348,12 +348,12 @@ open_flags |= O_RDWR; } else if (flags & FLAG_WRITE) { open_flags |= O_WRONLY; - } else if (!(flags & FLAG_READ)) { + } else if (flags & FLAG_READ) { + open_flags |= O_RDONLY; + } else { NOTREACHED(); } - static_assert(O_RDONLY == 0, "O_RDONLY must equal zero"); - int mode = S_IRUSR | S_IWUSR; int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode)); @@ -362,9 +362,6 @@ return; } - if (flags & FLAG_CREATE_ALWAYS) - created_ = true; - error_details_ = FILE_OK; file_.reset(descriptor); }
diff --git a/src/base/files/file_util_posix.cc b/src/base/files/file_util_posix.cc index 7ee1645..d7ea938 100644 --- a/src/base/files/file_util_posix.cc +++ b/src/base/files/file_util_posix.cc
@@ -14,7 +14,6 @@ #include <stdlib.h> #include <string.h> #include <sys/mman.h> -#include <sys/param.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/types.h> @@ -47,6 +46,10 @@ #include <grp.h> #endif +#if !defined(OS_ZOS) +#include <sys/param.h> +#endif + // We need to do this on AIX due to some inconsistencies in how AIX // handles XOPEN_SOURCE and ALL_SOURCE. #if defined(OS_AIX) @@ -58,7 +61,8 @@ namespace { #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ - defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ANDROID) && __ANDROID_API__ < 21 + defined(OS_HAIKU) || defined(OS_MSYS) || defined(OS_ZOS) || \ + defined(OS_ANDROID) && __ANDROID_API__ < 21 int CallStat(const char* path, stat_wrapper_t* sb) { return stat(path, sb); } @@ -143,6 +147,7 @@ // Appends |mode_char| to |mode| before the optional character set encoding; see // https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for // details. +#if !defined(OS_ZOS) std::string AppendModeCharacter(std::string_view mode, char mode_char) { std::string result(mode); size_t comma_pos = result.find(','); @@ -150,8 +155,9 @@ mode_char); return result; } -#endif +#endif // !OS_ZOS +#endif // !OS_MACOSX } // namespace FilePath MakeAbsoluteFilePath(const FilePath& input) { @@ -382,11 +388,25 @@ // this should be OK since mkdtemp just replaces characters in place char* buffer = const_cast<char*>(sub_dir_string.c_str()); +#if !defined(OS_ZOS) char* dtemp = mkdtemp(buffer); if (!dtemp) { DPLOG(ERROR) << "mkdtemp"; return false; } +#else + // TODO(gabylb) - zos: currently no mkdtemp on z/OS. + // Get a unique temp filename, which should also be unique as a directory name + char* dtemp = mktemp(buffer); + if (!dtemp) { + DPLOG(ERROR) << "mktemp"; + return false; + } + if (mkdir(dtemp, S_IRWXU)) { + DPLOG(ERROR) << "mkdir"; + return false; + } +#endif *new_dir = FilePath(dtemp); return true; } @@ -482,7 +502,7 @@ strchr(mode, 'e') == nullptr || (strchr(mode, ',') != nullptr && strchr(mode, 'e') > strchr(mode, ','))); FILE* result = nullptr; -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) || defined(OS_ZOS) // macOS does not provide a mode character to set O_CLOEXEC; see // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/fopen.3.html. const char* the_mode = mode; @@ -493,7 +513,7 @@ do { result = fopen(filename.value().c_str(), the_mode); } while (!result && errno == EINTR); -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) || defined(OS_ZOS) // Mark the descriptor as close-on-exec. if (result) SetCloseOnExec(fileno(result));
diff --git a/src/base/logging.cc b/src/base/logging.cc index ee60c47..ee9e109 100644 --- a/src/base/logging.cc +++ b/src/base/logging.cc
@@ -28,13 +28,15 @@ #if defined(OS_POSIX) || defined(OS_FUCHSIA) #include <errno.h> -#include <paths.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> +#if !defined(OS_ZOS) +#include <paths.h> +#endif #endif #include <algorithm>
diff --git a/src/gn/args.cc b/src/gn/args.cc index c21beea..2b67313 100644 --- a/src/gn/args.cc +++ b/src/gn/args.cc
@@ -320,6 +320,8 @@ os = "solaris"; #elif defined(OS_NETBSD) os = "netbsd"; +#elif defined(OS_ZOS) + os = "zos"; #else #error Unknown OS type. #endif
diff --git a/src/gn/exec_process.cc b/src/gn/exec_process.cc index cc778d8..69d631e 100644 --- a/src/gn/exec_process.cc +++ b/src/gn/exec_process.cc
@@ -23,6 +23,7 @@ #include <errno.h> #include <fcntl.h> #include <signal.h> +#include <sys/time.h> #include <sys/wait.h> #include <unistd.h>
diff --git a/src/gn/function_write_file_unittest.cc b/src/gn/function_write_file_unittest.cc index 5ee6123..8a8ef9a 100644 --- a/src/gn/function_write_file_unittest.cc +++ b/src/gn/function_write_file_unittest.cc
@@ -15,6 +15,8 @@ #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_HAIKU) || defined(OS_MSYS) #include <sys/time.h> +#elif defined(OS_ZOS) +#include <utime.h> #endif #if defined(OS_WIN) @@ -92,6 +94,8 @@ #elif defined(OS_AIX) || defined(OS_HAIKU) || defined(OS_SOLARIS) struct timeval times[2] = {}; ASSERT_EQ(utimes(foo_name.value().c_str(), times), 0); +#elif defined(OS_ZOS) + ASSERT_EQ(utime(foo_name.value().c_str(), NULL), 0); #else struct timeval times[2] = {}; ASSERT_EQ(futimes(foo_file.GetPlatformFile(), times), 0);
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc index 6bcb204..b4b71d6 100644 --- a/src/gn/ninja_rust_binary_target_writer_unittest.cc +++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -926,10 +926,14 @@ target.source_types_used().Set(SourceFile::SOURCE_RS); target.rust_values().set_crate_root(main); target.rust_values().crate_name() = "foo_bar"; + + const char* lib = "lib1"; target.config_values().externs().push_back( - std::pair("lib1", LibFile(SourceFile("//foo/lib1.rlib")))); + std::pair(lib, LibFile(SourceFile("//foo/lib1.rlib")))); + lib = "lib2"; target.config_values().externs().push_back( - std::pair("lib2", LibFile("lib2.rlib"))); + std::pair(lib, LibFile("lib2.rlib"))); + target.SetToolchain(setup.toolchain()); ASSERT_TRUE(target.OnResolved(&err));
diff --git a/src/gn/string_atom.cc b/src/gn/string_atom.cc index 7842fdd..5701a96 100644 --- a/src/gn/string_atom.cc +++ b/src/gn/string_atom.cc
@@ -204,11 +204,22 @@ KeySet local_set_; }; +#if !defined(OS_ZOS) thread_local ThreadLocalCache s_local_cache; +#else +// TODO(gabylb) - zos: thread_local not yet supported, use zoslib's impl'n: +static ThreadLocalCache s_tlc; +__tlssim<ThreadLocalCache*> __g_s_local_cache_impl(&s_tlc); +#define s_local_cache (*__g_s_local_cache_impl.access()) +#endif } // namespace StringAtom::StringAtom() : value_(kEmptyString) {} StringAtom::StringAtom(std::string_view str) noexcept +#ifndef OS_ZOS : value_(*s_local_cache.find(str)) {} +#else + : value_(*s_local_cache->find(str)) {} +#endif
diff --git a/src/util/build_config.h b/src/util/build_config.h index 667f009..5c4793e 100644 --- a/src/util/build_config.h +++ b/src/util/build_config.h
@@ -56,6 +56,9 @@ #define OS_ASMJS 1 #elif defined(__HAIKU__) #define OS_HAIKU 1 +#elif defined(__MVS__) +#include "zos-base.h" +#define OS_ZOS 1 #else #error Please add support for your platform in build_config.h #endif @@ -74,7 +77,7 @@ defined(OS_FREEBSD) || defined(OS_LINUX) || defined(OS_MACOSX) || \ defined(OS_NACL) || defined(OS_NETBSD) || defined(OS_OPENBSD) || \ defined(OS_QNX) || defined(OS_SOLARIS) || defined(OS_HAIKU) || \ - defined(OS_MSYS) + defined(OS_MSYS) || defined(OS_ZOS) #define OS_POSIX 1 #endif
diff --git a/src/util/exe_path.cc b/src/util/exe_path.cc index b67318c..7ae9024 100644 --- a/src/util/exe_path.cc +++ b/src/util/exe_path.cc
@@ -104,6 +104,16 @@ return base::FilePath(raw); } +#elif defined(OS_ZOS) + +base::FilePath GetExePath() { + char path[PATH_MAX]; + if (__getexepath(path, sizeof(path), getpid()) != 0) { + return base::FilePath(); + } + return base::FilePath(path); +} + #else base::FilePath GetExePath() {
diff --git a/src/util/msg_loop.cc b/src/util/msg_loop.cc index 1566532..1797c90 100644 --- a/src/util/msg_loop.cc +++ b/src/util/msg_loop.cc
@@ -8,7 +8,13 @@ namespace { +#if !defined(OS_ZOS) thread_local MsgLoop* g_current; +#else +// TODO(gabylb) - zos: thread_local not yet supported, use zoslib's impl'n: +__tlssim<MsgLoop*> __g_current_impl(nullptr); +#define g_current (*__g_current_impl.access()) +#endif } MsgLoop::MsgLoop() {
diff --git a/src/util/semaphore.h b/src/util/semaphore.h index 2952cae..f74b8f8 100644 --- a/src/util/semaphore.h +++ b/src/util/semaphore.h
@@ -15,6 +15,8 @@ #include <windows.h> #elif defined(OS_MACOSX) #include <dispatch/dispatch.h> +#elif defined(OS_ZOS) +#include "zos-semaphore.h" #elif defined(OS_POSIX) #include <semaphore.h> #else
diff --git a/src/util/sys_info.cc b/src/util/sys_info.cc index 8124e07..c8dc590 100644 --- a/src/util/sys_info.cc +++ b/src/util/sys_info.cc
@@ -36,6 +36,8 @@ arch = "x86_64"; } else if (os == "AIX" || os == "OS400") { arch = "ppc64"; + } else if (std::string(info.sysname) == "OS/390") { + arch = "s390x"; } return arch; #elif defined(OS_WIN) @@ -56,7 +58,10 @@ } int NumberOfProcessors() { -#if defined(OS_POSIX) +#if defined(OS_ZOS) + return __get_num_online_cpus(); + +#elif defined(OS_POSIX) // sysconf returns the number of "logical" (not "physical") processors on both // Mac and Linux. So we get the number of max available "logical" processors. //