Showing 8 posts
Bazel’s original raison d’etre was to support Google’s monorepo. A consequence of using a monorepo is that some builds will become very large. And large builds can be very resource hungry, especially when using a tool like Bazel that tries to parallelize as many actions as possible for efficiency reasons. There are many resource types in a system, but today I’d like to focus on the number of open files at any given time (nofiles
).
January 29, 2019
·
Tags:
bazel, jvm, macos, monorepo, portability
Continue reading (about
3 minutes)
Many programming guides recommend to begin scripts with the #! /usr/bin/env
shebang in order to to automatically locate the necessary interpreter. For example, for a Python script you would use #! /usr/bin/env python
, and then the saying goes, the script would “just work” on any machine with Python installed.
The reason for this recommendation is that /usr/bin/env python
will search the PATH
for a program called python
and execute the first one found… and that usually works fine on one’s own machine.
September 14, 2016
·
Tags:
featured, portability, programming, scripts, unix
Continue reading (about
5 minutes)
Update (2014-12-19): The advice provided in this blog post is questionable and, in fact, probably incorrect. The bug described below must have happened for some unrelated reason (like, maybe, reuse of ap), but at this point (three years later!) I do not really remember what was going on here nor have much interest in retrying.
A long time ago, while I was preparing an ATF release, I faced many failing tests and crashes in one of the platforms under test. My memory told me this was a problem in OpenSolaris, but the repository logs say that the problem really happened in Fedora 8 x86_64.voidThe codebase of ATF provides _fmt and _ap variants for many functions to give more flexibility to the caller and, as shown above, the _fmt variant just relies on the _ap variant to do the real work.
foo_fmt(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
foo_ap(fmt, ap);
va_end(ap);
}
void
foo_ap(const char *fmt, va_list ap)
{
char buf[128];
vsnprintf(buf, sizeof(buf), fmt, ap);
... now, do something with buf ...
}
voidThis duplication of the ap argument pointing to the variable list of arguments ensures that ap2 can be safely used from the new stack frame.
foo_ap(const char *fmt, va_list ap)
{
char buf[128];
va_list ap2;
va_copy(ap2, ap);
vsnprintf(buf, sizeof(buf), fmt, ap2);
va_end(ap2);
... now, do something with buf ...
}
September 12, 2011
·
Tags:
c, portability
Continue reading (about
3 minutes)
Some implementations of test(1), in an attempt to be smart, provide non-standard operators such as ==. Please forget about those: they make your scripts non-portable and a pain to use in other systems. Why? Because, due to the way the shell works, failures in calls to test(1) will often just result in an error message (which may not be seen due to other output) and the script will happily continue running even if it missed to perform some important operation.
So... just use the standard equality operators:
April 24, 2010
·
Tags:
portability
Continue reading (about
1 minute)
If you use #ifdef conditionals in your code to check for portability features, be sure to always define a catch-all else clause that actually does something, even if this something is to error out.
Consider the following code snippet, quoted from gamin's tests/testing.c file:
if (arg != NULL) {The FAMOpen method queries the GAM_CLIENT_ID environment variable to set up the connections parameters to the FAM server. If the variable is not defined, the connection will still work, even though it will use some default internal value. In the test code above, the variable is explicitly set to let the tests use a separate server instance.
#ifdef HAVE_SETENV
setenv("GAM_CLIENT_ID", arg, 1);
#elif HAVE_PUTENV
char *client_id = malloc (strlen (arg) + sizeof "GAM_CLIENT_ID=");
if (client_id)
{
strcpy (client_id, "GAM_CLIENT_ID=");
strcat (client_id, arg);
putenv (client_id);
}
#endif /* HAVE_SETENV */
}
ret = FAMOpen(&(testState.fc));
#if defined(HAVE_SETENV)With this code, we can be sure that the code will not build if none of the possible implementations are selected. We can later proceed to investigate why that happened.
setenv(...);
#elif defined(HAVE_PUTENV)
putenv(...);
#else
# error "Don't know how to set environment variables."
#endif
April 22, 2010
·
Tags:
portability
Continue reading (about
3 minutes)
I have always thought that unlink(2) was meant to remove files only but, yesterday, SunOS (SXDE 200709) proved my wrong. I was sanity-checking the source tree for the imminent ATF 0.4 release under this platform, which is always scary, and the tests for the atf::fs::remove function were failing — only when run as root.
The failure happened in the cleanup phase of the test case, in which ATF attempts to recursively remove the temporary work directory. When it attempted to remove one of the directories inside it, it failed with a ENOENT message, which in SunOS may mean that the directory is not empty. Strangely, when inspecting the left-over work tree, that directory was indeed empty and it could not be removed with rm -rf nor with rmdir.
The manual page for unlink(2) finally gave me the clue of what was happening:
If the path argument is a directory and the filesystem supports unlink() and unlinkat() on directories, the directory is unlinked from its parent with no cleanup being performed. In UFS, the disconnected directory will be found the next time the filesystem is checked with fsck(1M). The unlink() and unlinkat() functions will not fail simply because a directory is not empty. The user with appropriate privileges can orphan a non-empty directory without generating an error message.The solution was easy: as my custom remove function is supposed to remove files only, I added a check before the call to unlink(2) to ensure that the path name does not point to a directory. Not the prettiest possibility (because it is subject to race-conditions even though it is not critical), but it works.
February 3, 2008
·
Tags:
atf, portability, sunos
Continue reading (about
2 minutes)
Yesterday I mentioned the need for a way to kill a tree of processes in order to effectively implement timeouts for test cases. Let's see how the current algorithm in ATF works:
January 16, 2008
·
Tags:
atf, portability, process
Continue reading (about
3 minutes)
Today, I've attempted to build atf on a NetBSD 4.0_BETA2 system I've been setting up in a spare box I had around, as opposed to the Mac OS X system I'm using for daily development. The build failed due to some well-understood problems, but there was an annoying one with respect to some calls to the standard XPG basename(3) and dirname(3) functions.
According to the Mac OS X manual pages for those functions, they are supposed to take a const char * argument. However, the NetBSD versions of these functions take a plain char * parameter instead — i.e., not a constant pointer.
After Googling for some references and with advice from joerg@, I got the answer: it turns out that the XPG versions1 of basename and dirname can modify the input string by trimming trailing directory separators (even though the current implementation in NetBSD does not do that). This makes no sense to me, but it's what the XPG4.2 and POSIX.1 standards specify.
I've resolved this issue by simply re-implementing basename and dirname (which I wanted to do anyway), making my own versions take and return constant strings. And to make things safer, I've added a check to the configure script that verifies if the basename and dirname implementations take a constant pointer and, in that (incorrect) case, use the standard functions to sanity-check the results of my own by means of an assertion.
1 How not, the GNU libc library provides its own variations of basename and dirname. However, including libgen.h forces the usage of the XPG versions.
June 28, 2007
·
Tags:
atf, portability, soc
Continue reading (about
2 minutes)