On Android, update std.datetime to extract timezone data and some other fixes.

This commit is contained in:
Joakim 2016-02-08 01:05:21 +05:30
parent 679298a7a9
commit 10c01a0614
6 changed files with 159 additions and 61 deletions

View file

@ -28494,7 +28494,12 @@ public:
}
version(Posix)
version(Android)
{
// Android concatenates all time zone data into a single file and stores it here.
enum defaultTZDatabaseDir = "/system/usr/share/zoneinfo/";
}
else version(Posix)
{
/++
The default directory where the TZ Database files are. It's empty
@ -28549,13 +28554,22 @@ public:
enforce(tzDatabaseDir.exists(), new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir)));
enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir)));
const file = asNormalizedPath(chainPath(tzDatabaseDir, name)).to!string;
version(Android)
{
auto tzfileOffset = name in tzdataIndex(tzDatabaseDir);
enforce(tzfileOffset, new DateTimeException(format("The time zone %s is not listed.", name)));
string tzFilename = separate_index ? "zoneinfo.dat" : "tzdata";
const file = asNormalizedPath(chainPath(tzDatabaseDir, tzFilename)).to!string;
}
else
const file = asNormalizedPath(chainPath(tzDatabaseDir, name)).to!string;
enforce(file.exists(), new DateTimeException(format("File %s does not exist.", file)));
enforce(file.isFile, new DateTimeException(format("%s is not a file.", file)));
auto tzFile = File(file);
immutable gmtZone = file.representation().canFind("GMT");
version(Android) tzFile.seek(*tzfileOffset);
immutable gmtZone = name.representation().canFind("GMT");
try
{
@ -28739,8 +28753,16 @@ public:
auto posixEnvStr = tzFile.readln().strip();
_enforceValidTZFile(tzFile.readln().strip().empty);
_enforceValidTZFile(tzFile.eof);
version(Android)
{
// Android uses a single file for all timezone data, so the file
// doesn't end here.
}
else
{
_enforceValidTZFile(tzFile.readln().strip().empty);
_enforceValidTZFile(tzFile.eof);
}
auto transitionTypes = new TransitionType*[](tempTTInfos.length);
@ -28889,20 +28911,31 @@ public:
auto timezones = appender!(string[])();
foreach(DirEntry dentry; dirEntries(tzDatabaseDir, SpanMode.depth))
version(Android)
{
if(dentry.isFile)
import std.algorithm : copy, filter;
tzdataIndex(tzDatabaseDir)
.byKey
.filter!(a => a.startsWith(subName))
.copy(timezones);
}
else
{
foreach(DirEntry dentry; dirEntries(tzDatabaseDir, SpanMode.depth))
{
auto tzName = dentry.name[tzDatabaseDir.length .. $];
if(!tzName.extension().empty ||
!tzName.startsWith(subName) ||
tzName == "+VERSION")
if(dentry.isFile)
{
continue;
}
auto tzName = dentry.name[tzDatabaseDir.length .. $];
timezones.put(tzName);
if(!tzName.extension().empty ||
!tzName.startsWith(subName) ||
tzName == "+VERSION")
{
continue;
}
timezones.put(tzName);
}
}
}
@ -28932,6 +28965,8 @@ public:
foreach(tzName; tzNames)
assertNotThrown!DateTimeException(testPTZSuccess(tzName));
// No timezone directories on Android, just a single tzdata file
version(Android) {} else
foreach(DirEntry dentry; dirEntries(defaultTZDatabaseDir, SpanMode.depth))
{
if(dentry.isFile)
@ -29161,6 +29196,77 @@ private:
_hasDST = hasDST;
}
// Android concatenates the usual timezone directories into a single file,
// tzdata, along with an index to jump to each timezone's offset. In older
// versions of Android, the index was stored in a separate file, zoneinfo.idx,
// whereas now it's stored at the beginning of tzdata.
version(Android)
{
// Keep track of whether there's a separate index, zoneinfo.idx. Only
// check this after calling tzdataIndex, as it's initialized there.
static shared bool separate_index;
// Extracts the name of each time zone and the offset where its data is
// located in the tzdata file from the index and caches it for later.
static const(uint[string]) tzdataIndex(string tzDir)
{
import std.concurrency : initOnce;
static __gshared uint[string] _tzIndex;
// _tzIndex is initialized once and then shared across all threads.
initOnce!_tzIndex(
{
import std.conv : to;
import std.format : format;
import std.path : asNormalizedPath, chainPath;
enum indexEntrySize = 52;
const combinedFile = asNormalizedPath(chainPath(tzDir, "tzdata")).to!string;
const indexFile = asNormalizedPath(chainPath(tzDir, "zoneinfo.idx")).to!string;
File tzFile;
uint indexEntries, dataOffset;
uint[string] initIndex;
// Check for the combined file tzdata, which stores the index
// and the time zone data together.
if(combinedFile.exists() && combinedFile.isFile)
{
tzFile = File(combinedFile);
_enforceValidTZFile(readVal!(char[])(tzFile, 6) == "tzdata");
auto tzDataVersion = readVal!(char[])(tzFile, 6);
_enforceValidTZFile(tzDataVersion[5] == '\0');
uint indexOffset = readVal!uint(tzFile);
dataOffset = readVal!uint(tzFile);
readVal!uint(tzFile);
indexEntries = (dataOffset - indexOffset)/indexEntrySize;
separate_index = false;
}
else if(indexFile.exists() && indexFile.isFile)
{
tzFile = File(indexFile);
indexEntries = to!(uint)(tzFile.size/indexEntrySize);
separate_index = true;
}
else
throw new DateTimeException(format("Both timezone files %s and %s do not exist.",
combinedFile, indexFile));
foreach(Unused; 0 .. indexEntries) {
string tzName = to!string(readVal!(char[])(tzFile, 40).ptr);
uint tzOffset = readVal!uint(tzFile);
readVal!(uint[])(tzFile, 2);
initIndex[tzName] = dataOffset + tzOffset;
}
initIndex.rehash;
return initIndex;
}());
return _tzIndex;
}
}
/// List of times when the utc offset changes.
immutable Transition[] _transitions;
@ -29655,7 +29761,10 @@ else version(Posix)
import core.sys.posix.stdlib : setenv;
import core.sys.posix.time : tzset;
auto value = asNormalizedPath(chainPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName));
version(Android)
auto value = asNormalizedPath(tzDatabaseName);
else
auto value = asNormalizedPath(chainPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName));
setenv("TZ", value.tempCString(), 1);
tzset();
}

View file

@ -664,15 +664,12 @@ unittest
alloc.reallocate(b, 20);
alloc.deallocate(b);
import std.file : deleteme, remove;
import std.stdio : File;
import std.range : walkLength;
version(Posix)
auto f = "/tmp/dlang.std.experimental.allocator.stats_collector.txt";
version(Windows)
{
import std.process: environment;
auto f = environment.get("temp") ~ r"\dlang.std.experimental.allocator.stats_collector.txt";
}
auto f = deleteme ~ "-dlang.std.experimental.allocator.stats_collector.txt";
scope(exit) remove(f);
Allocator.reportPerCallStatistics(File(f, "w"));
alloc.reportStatistics(File(f, "a"));
assert(File(f).byLine.walkLength == 22);

View file

@ -3118,6 +3118,7 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
else version(Posix)
{
import core.stdc.stdio;
static import std.conv;
immutable fd = core.sys.posix.fcntl.open(fromz, O_RDONLY);
cenforce(fd != -1, f, fromz);
@ -3158,7 +3159,7 @@ private void copyImpl(const(char)[] f, const(char)[] t, const(FSChar)* fromz, co
size -= toxfer;
}
if (preserve)
cenforce(fchmod(fdw, statbuf.st_mode) == 0, f, fromz);
cenforce(fchmod(fdw, std.conv.to!mode_t(statbuf.st_mode)) == 0, f, fromz);
}
cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
@ -3902,6 +3903,11 @@ string tempDir() @trusted
DWORD len = GetTempPathW(buf.length, buf.ptr);
if (len) cache = toUTF8(buf[0 .. len]);
}
else version(Android)
{
// Don't check for a global temporary directory as
// Android doesn't have one.
}
else version(Posix)
{
import std.process : environment;

View file

@ -1793,9 +1793,7 @@ unittest
formatTest( to!( const T)(5.5), "5.5" );
formatTest( to!(immutable T)(5.5), "5.5" );
// bionic doesn't support lower-case string formatting of nan yet
version(CRuntime_Bionic) { formatTest( T.nan, "NaN" ); }
else { formatTest( T.nan, "nan" ); }
formatTest( T.nan, "nan" );
}
}
@ -3722,13 +3720,6 @@ unittest
|| stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", // MSVCRT 14+ (VS 2015)
stream.data);
}
else version (CRuntime_Bionic)
{
// bionic doesn't support hex formatting of floating point numbers
// or lower-case string formatting of nan yet, but it was committed
// recently (April 2014):
// https://code.google.com/p/android/issues/detail?id=64886
}
else
{
assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
@ -3757,12 +3748,6 @@ unittest
version (CRuntime_Microsoft)
assert(stream.data == "0x1.51eb85p+0 0X1.B1EB86P+2"
|| stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB860000000P+2"); // MSVCRT 14+ (VS 2015)
else version (CRuntime_Bionic)
{
// bionic doesn't support hex formatting of floating point numbers,
// but it was committed recently (April 2014):
// https://code.google.com/p/android/issues/detail?id=64886
}
else
assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2");
stream.clear();
@ -6118,13 +6103,6 @@ unittest
else version (CRuntime_Microsoft)
assert(s == "1.67 -0X1.47AE14P+0 nan"
|| s == "1.67 -0X1.47AE147AE147BP+0 nan", s); // MSVCRT 14+ (VS 2015)
else version (CRuntime_Bionic)
{
// bionic doesn't support hex formatting of floating point numbers
// or lower-case string formatting of nan yet, but it was committed
// recently (April 2014):
// https://code.google.com/p/android/issues/detail?id=64886
}
else
assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s);

View file

@ -724,10 +724,10 @@ private bool isExecutable(in char[] path) @trusted nothrow @nogc //TODO: @safe
version (Posix) unittest
{
import std.algorithm;
auto unamePath = searchPathFor("uname");
assert (!unamePath.empty);
assert (unamePath[0] == '/');
assert (unamePath.endsWith("uname"));
auto lsPath = searchPathFor("ls");
assert (!lsPath.empty);
assert (lsPath[0] == '/');
assert (lsPath.endsWith("ls"));
auto unlikely = searchPathFor("lkmqwpoialhggyaofijadsohufoiqezm");
assert (unlikely is null, "Are you kidding me?");
}
@ -1474,7 +1474,12 @@ unittest // tryWait() and kill()
TestScript prog = "while true; do sleep 1; done";
}
auto pid = spawnProcess(prog.path);
Thread.sleep(dur!"seconds"(1));
// Android appears to automatically kill sleeping processes very quickly,
// so shorten the wait before killing here.
version (Android)
Thread.sleep(dur!"msecs"(5));
else
Thread.sleep(dur!"seconds"(1));
kill(pid);
version (Windows) assert (wait(pid) == 1);
else version (Posix) assert (wait(pid) == -SIGTERM);
@ -2147,6 +2152,10 @@ unittest
"echo|set /p=%~1
echo|set /p=%~2 1>&2
exit 123";
else version (Android) TestScript prog =
`echo -n $1
echo -n $2 >&2
exit 123`;
else version (Posix) TestScript prog =
`printf '%s' $1
printf '%s' $2 >&2
@ -2239,17 +2248,15 @@ $(LREF nativeShell).
*/
@property string userShell() @safe
{
version (Windows) return environment.get("COMSPEC", "cmd.exe");
else version (Android) return environment.get("SHELL", "/system/bin/sh");
else version (Posix) return environment.get("SHELL", "/bin/sh");
version (Windows) return environment.get("COMSPEC", nativeShell);
else version (Posix) return environment.get("SHELL", nativeShell);
}
/**
The platform-specific native shell path.
On Windows, this function returns $(D "cmd.exe").
On POSIX, $(D nativeShell) returns $(D "/bin/sh").
This function returns $(D "cmd.exe") on Windows, $(D "/bin/sh") on POSIX, and
$(D "/system/bin/sh") on Android.
*/
@property string nativeShell() @safe @nogc pure nothrow
{
@ -2344,8 +2351,7 @@ private struct TestScript
else version (Posix)
{
auto ext = "";
version(Android) auto firstLine = "#!" ~ userShell;
else auto firstLine = "#!/bin/sh";
auto firstLine = "#!" ~ nativeShell;
}
path = uniqueTempPath()~ext;
std.file.write(path, firstLine~std.ascii.newline~code~std.ascii.newline);

View file

@ -466,9 +466,11 @@ class Protocol
}
// Skip this test on Android because getprotobyname/number are
// unimplemented in bionic.
version(CRuntime_Bionic) {} else
unittest
{
// getprotobyname,number are unimplemented in bionic
softUnittest({
Protocol proto = new Protocol;
assert(proto.getProtocolByType(ProtocolType.TCP));