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.
//