diff --git a/CMakeLists.txt b/CMakeLists.txt index 739edeb..4cf9ef9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,7 @@ if(WIN32) # TODO fix and use windows flags set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WINDOWS_LINKER_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WINDOWS_FLAGS}") endif() set(OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/output") diff --git a/README.md b/README.md index 0f0c9e8..e634838 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ NFHTTP [![CircleCI](https://circleci.com/gh/spotify/NFHTTP/tree/master.svg?style=svg)](https://circleci.com/gh/spotify/NFHTTP/tree/master) +[![Build status](https://ci.appveyor.com/api/projects/status/57i0pfle7okccm3q/branch/master?svg=true)](https://ci.appveyor.com/project/8W9aG/nfhttp/branch/master) [![License](https://img.shields.io/github/license/spotify/NFHTTP.svg)](LICENSE) [![Spotify FOSS Slack](https://slackin.spotify.com/badge.svg)](https://slackin.spotify.com) [![Readme Score](http://readme-score-api.herokuapp.com/score.svg?url=https://github.com/spotify/nfhttp)](http://clayallsopp.github.io/readme-score?url=https://github.com/spotify/nfhttp) diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..485d2ff --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,10 @@ +image: +- Visual Studio 2017 + +build_script: +- git submodule update --init --recursive +- ps: ci/windows.ps1 + +artifacts: + - path: build/output/libNFHTTP.zip + name: libNFHTTP.zip diff --git a/ci/nfbuildwindows.py b/ci/nfbuildwindows.py index 0999e17..ee61fce 100644 --- a/ci/nfbuildwindows.py +++ b/ci/nfbuildwindows.py @@ -17,104 +17,8 @@ class NFBuildWindows(NFBuild): def __init__(self): super(self.__class__, self).__init__() self.project_file = 'build.ninja' - - def installClangFormat(self): - clang_format_vulcan_file = os.path.join('tools', 'clang-format.vulcan') - clang_format_extraction_folder = self.vulcanDownload( - clang_format_vulcan_file, - 'clang-format-5.0.0') - self.clang_format_binary = os.path.join( - os.path.join( - os.path.join( - clang_format_extraction_folder, - 'clang-format'), - 'bin'), - 'clang-format') - - def installNinja(self): - ninja_vulcan_file = os.path.join( - os.path.join( - os.path.join( - os.path.join('tools', 'buildtools'), - 'spotify_buildtools'), - 'software'), - 'ninja.vulcan') - ninja_extraction_folder = self.vulcanDownload( - ninja_vulcan_file, - 'ninja-1.6.0') - self.ninja_binary = os.path.join( - ninja_extraction_folder, - 'ninja') - if 'PATH' not in os.environ: - os.environ['PATH'] = '' - if len(os.environ['PATH']) > 0: - os.environ['PATH'] += os.pathsep - os.environ['PATH'] += ninja_extraction_folder - - def installMake(self): - make_vulcan_file = os.path.join('tools', 'make.vulcan') - make_extraction_folder = self.vulcanDownload( - make_vulcan_file, - 'make-4.2.1') - make_bin_folder = os.path.join( - make_extraction_folder, - 'bin') - os.environ['PATH'] += os.pathsep + make_bin_folder - - def installVisualStudio(self): - vs_vulcan_file = os.path.join( - os.path.join( - os.path.join( - os.path.join('tools', 'buildtools'), - 'spotify_buildtools'), - 'software'), - 'visualstudio.vulcan') - self.vs_extraction_folder = self.vulcanDownload( - vs_vulcan_file, - 'visualstudio-2017') - sdk_version = '10.0.15063.0' - vc_tools_version = '14.10.25017' - vc_redist_version = '14.10.25008' - vc_redist_crt = 'Microsoft.VC150.CRT' - vs_root = self.vs_extraction_folder - sdk_root = os.path.join(vs_root, 'win10sdk') - vc_root = os.path.join(vs_root, 'VC') - vc_tools_root = os.path.join(vc_root, 'Tools', 'MSVC') - vc_redist_root = os.path.join(vc_root, 'Redist', 'MSVC') - os.environ['VS_ROOT'] = vs_root - os.environ['SDK_ROOT'] = sdk_root - os.environ['INCLUDE'] = os.pathsep.join([ - os.path.join(sdk_root, 'Include', sdk_version, 'um'), - os.path.join(sdk_root, 'Include', sdk_version, 'ucrt'), - os.path.join(sdk_root, 'Include', sdk_version, 'shared'), - os.path.join(sdk_root, 'Include', sdk_version, 'winrt'), - os.path.join(vc_tools_root, vc_tools_version, 'include'), - os.path.join(vc_tools_root, vc_tools_version, 'atlmfc', 'include'), - os.environ.get('INCLUDE', '')]) - os.environ['PATH'] = os.pathsep.join([ - os.path.join(sdk_root, 'bin', sdk_version, 'x86'), - os.path.join(vc_tools_root, vc_tools_version, 'bin', 'HostX64', 'x86'), - os.path.join(vc_tools_root, vc_tools_version, 'bin', 'HostX64', 'x64'), - os.path.join(vc_redist_root, vc_redist_version, 'x64', vc_redist_crt), - os.path.join(vs_root, 'SystemCRT'), - os.environ.get('PATH', '')]) - os.environ['LIB'] = os.pathsep.join([ - os.path.join(sdk_root, 'Lib', sdk_version, 'um', 'x86'), - os.path.join(sdk_root, 'Lib', sdk_version, 'ucrt', 'x86'), - os.path.join(vc_tools_root, vc_tools_version, 'lib', 'x86'), - os.path.join(vc_tools_root, vc_tools_version, 'atlmfc', 'lib', 'x86'), - os.environ.get('LIB', '')]) - os.environ['LIBPATH'] = os.pathsep.join([ - os.path.join(vc_tools_root, vc_tools_version, 'lib', 'x86', 'store', 'references'), - os.path.join(sdk_root, 'UnionMetadata', sdk_version), - os.environ.get('LIBPATH', '')]) - - def installVulcanDependencies(self, android=False): - super(self.__class__, self).installVulcanDependencies(android) - self.installClangFormat() - self.installMake() - self.installVisualStudio() - self.installNinja() + self.cmake_binary = 'cmake' + self.android = False def generateProject(self, ios=False, @@ -124,7 +28,7 @@ def generateProject(self, cmake_call = [ self.cmake_binary, '..', - '-GNinja'] + '-G'] if android or android_arm: android_abi = 'x86_64' android_toolchain_name = 'x86_64-llvm' @@ -132,6 +36,7 @@ def generateProject(self, android_abi = 'arm64-v8a' android_toolchain_name = 'arm64-llvm' cmake_call.extend([ + 'Ninja', '-DANDROID=1', '-DCMAKE_TOOLCHAIN_FILE=' + self.android_ndk_folder + '/build/cmake/android.toolchain.cmake', '-DANDROID_NDK=' + self.android_ndk_folder, @@ -142,27 +47,29 @@ def generateProject(self, '-DANDROID_STL=c++_shared']) self.project_file = 'build.ninja' else: - cl_exe = os.path.join(self.vs_extraction_folder, 'VC', 'Tools', 'MSVC', '14.10.25017', 'bin', 'HostX64', 'x86', 'cl.exe').replace('\\', '/') - rc_exe = os.path.join(self.vs_extraction_folder, 'win10sdk', 'bin', '10.0.15063.0', 'x64', 'rc.exe').replace('\\', '/') - link_exe = os.path.join(self.vs_extraction_folder, 'VC', 'Tools', 'MSVC', '14.10.25017', 'bin', 'HostX64', 'x86', 'link.exe').replace('\\', '/') cmake_call.extend([ - '-DCMAKE_C_COMPILER=' + cl_exe, - '-DCMAKE_CXX_COMPILER=' + cl_exe, - '-DCMAKE_RC_COMPILER=' + rc_exe, - '-DCMAKE_LINKER=' + link_exe, - '-DWINDOWS=1']) + 'Visual Studio 15 2017 Win64', + '-DCMAKE_SYSTEM_NAME=WindowsStore', + '-DCMAKE_SYSTEM_VERSION=10.0']) cmake_result = subprocess.call(cmake_call, cwd=self.build_directory) if cmake_result != 0: sys.exit(cmake_result) def buildTarget(self, target, sdk='macosx', arch='x86_64'): - result = subprocess.call([ - self.ninja_binary, - '-C', - self.build_directory, - '-f', - self.project_file, - target]) + result = 0 + if self.android: + result = subprocess.call([ + self.ninja_binary, + '-C', + self.build_directory, + '-f', + self.project_file, + target]) + else: + result = subprocess.call([ + 'msbuild.exe', + os.path.join(self.build_directory, 'NFHTTP.sln'), + '/t:NFHTTP;' + target]) if result != 0: sys.exit(result) @@ -192,3 +99,22 @@ def runIntegrationTests(self): if cli_result: sys.exit(cli_result) + def packageArtifacts(self): + lib_name = 'NFHTTP.lib' + cli_name = 'NFHTTPCLI.exe' + output_folder = os.path.join(self.build_directory, 'output') + artifacts_folder = os.path.join(output_folder, 'NFHTTP') + shutil.copytree('include', os.path.join(artifacts_folder, 'include')) + source_folder = os.path.join(self.build_directory, 'source') + lib_matches = self.find_file(source_folder, lib_name) + cli_matches = self.find_file(source_folder, cli_name) + shutil.copyfile(lib_matches[0], os.path.join(artifacts_folder, lib_name)) + if not self.android: + shutil.copyfile(cli_matches[0], os.path.join(artifacts_folder, cli_name)) + output_zip = os.path.join(output_folder, 'libNFHTTP.zip') + self.make_archive(artifacts_folder, output_zip) + if self.android: + final_zip_name = 'libNFHTTP-androidx86.zip' + if self.android_arm: + final_zip_name = 'libNFHTTP-androidArm64.zip' + shutil.copyfile(output_zip, final_zip_name) diff --git a/ci/windows.ps1 b/ci/windows.ps1 index ce5c670..aa93a8e 100644 --- a/ci/windows.ps1 +++ b/ci/windows.ps1 @@ -1,41 +1,66 @@ +<# + * Copyright (c) 2018 Spotify AB. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + #> param ( [string]$build = "windows" ) +Add-Type -AssemblyName System.IO.Compression.FileSystem + Write-Host "NFHTTP build process starting..." Write-Host $build -$ErrorActionPreference = "Stop" - try { - # Get python version - $python_version = python --version - Write-Host $python_version + # Upgrade pip or else the CI will complain + c:\python27\python.exe -m pip install --upgrade pip + + $BoostFoldername = Join-Path $PSScriptRoot "boost_1_64_0" + $BoostZipname = Join-Path $PSScriptRoot "boost_1_64_0.zip" + wget https://dl.bintray.com/boostorg/release/1.64.0/source/boost_1_64_0.zip -OutFile $BoostZipname + [System.IO.Compression.ZipFile]::ExtractToDirectory($BoostZipname, $BoostFoldername) + $env:BOOST_ROOT = $BoostFoldername # Start virtualenv - $virtualenv_vulcan_output = python tools/vulcan/bin/vulcan.py -v -f tools/virtualenv.vulcan -p virtualenv-15.1.0 - $virtualenv_bin = Join-Path $virtualenv_vulcan_output /virtualenv-15.1.0/virtualenv.py - python $virtualenv_bin nfdriver_env + pip install virtualenv + virtualenv nfhttp_env - & ./nfdriver_env/Scripts/activate.bat + & ./nfhttp_env/Scripts/activate.bat # Install Python Packages - & nfdriver_env/Scripts/pip.exe install urllib3 - & nfdriver_env/Scripts/pip.exe install pyyaml - & nfdriver_env/Scripts/pip.exe install flake8 + & nfhttp_env/Scripts/pip.exe install urllib3 ` + pyyaml ` + flake8 ` + cmakelint if($build -eq "android"){ - & nfdriver_env/Scripts/python.exe ci/androidwindows.py + & nfhttp_env/Scripts/python.exe ci/androidwindows.py } else { - & nfdriver_env/Scripts/python.exe ci/windows.py + & nfhttp_env/Scripts/python.exe ci/windows.py build } if($LASTEXITCODE -ne 0){ exit $LASTEXITCODE } - & ./nfdriver_env/Scripts/deactivate.bat + & ./nfhttp_env/Scripts/deactivate.bat } catch { diff --git a/ci/windows.py b/ci/windows.py index a027b15..90d5511 100644 --- a/ci/windows.py +++ b/ci/windows.py @@ -1,24 +1,71 @@ #!/usr/bin/env python +''' + * Copyright (c) 2018 Spotify AB. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +''' import sys from nfbuildwindows import NFBuildWindows +from build_options import BuildOptions def main(): + buildOptions = BuildOptions() + buildOptions.addOption("makeBuildDirectory", + "Wipe existing build directory") + buildOptions.addOption("generateProject", "Regenerate project") + buildOptions.addOption("buildTargetCLI", + "Build Target: CLI") + buildOptions.addOption("buildTargetLibrary", "Build Target: Library") + buildOptions.addOption("packageArtifacts", "Package the binary artifacts") + buildOptions.setDefaultWorkflow("Empty workflow", []) + + buildOptions.addWorkflow("build", "Production Build", [ + 'makeBuildDirectory', + 'generateProject', + 'buildTargetCLI', + 'buildTargetLibrary', + 'packageArtifacts' + ]) + + options = buildOptions.parseArgs() + buildOptions.verbosePrintBuildOptions(options) + library_target = 'NFHTTP' cli_target = 'NFHTTPCLI' nfbuild = NFBuildWindows() - nfbuild.build_print("Installing Dependencies") - nfbuild.installDependencies() - # Make our main build artifacts - nfbuild.build_print("C++ Build Start (x86)") - nfbuild.makeBuildDirectory() - nfbuild.generateProject() - targets = [library_target, cli_target] - for target in targets: - nfbuild.buildTarget(target) - # nfbuild.runIntegrationTests() + + if buildOptions.checkOption(options, 'makeBuildDirectory'): + nfbuild.makeBuildDirectory() + + if buildOptions.checkOption(options, 'generateProject'): + nfbuild.generateProject(ios=True) + + if buildOptions.checkOption(options, 'buildTargetLibrary'): + nfbuild.buildTarget(library_target) + + if buildOptions.checkOption(options, 'buildTargetCLI'): + nfbuild.buildTarget(cli_target) + + if buildOptions.checkOption(options, "packageArtifacts"): + nfbuild.packageArtifacts() if __name__ == "__main__": diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 42d5b94..da7db08 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -17,7 +17,7 @@ # specific language governing permissions and limitations # under the License. add_library(sqlite "${CMAKE_CURRENT_SOURCE_DIR}/sqlite/sqlite3.c") -target_compile_definitions(sqlite PUBLIC -DSQLITE_THREADSAFE=2) +target_compile_definitions(sqlite PUBLIC -DSQLITE_THREADSAFE=2 -DSQLITE_OS_WINRT=1) set(BOOST_MAJOR 1) set(BOOST_MINOR 64) @@ -1107,6 +1107,6 @@ if(USE_CPPRESTSDK) set(CPPREST_INSTALL_HEADERS OFF CACHE BOOL "Don't install headers" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build cpprest as a static lib" FORCE) set(CPPREST_EXCLUDE_COMPRESSION ON CACHE BOOL "Exclude compression functionality." FORCE) - add_definitions(-DSSL_R_SHORT_READ) + add_definitions(-DSSL_R_SHORT_READ -DNOMINMAX) add_subdirectory(cpprestsdk/Release) endif() diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b74b577..00aa355 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -116,3 +116,7 @@ target_link_libraries(NFHTTPCLI NFHTTP nlohmann_json) if(USE_CURL) target_compile_definitions(NFHTTP PRIVATE USE_CURL=1) endif() + +if(WIN32) + target_compile_definitions(NFHTTP PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() diff --git a/source/CacheLocationWindows.cpp b/source/CacheLocationWindows.cpp index 7cf2fcb..96b0214 100644 --- a/source/CacheLocationWindows.cpp +++ b/source/CacheLocationWindows.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include namespace nativeformat { namespace http { diff --git a/source/CachingSQLiteDatabase.cpp b/source/CachingSQLiteDatabase.cpp index 7fc6c50..b527c0b 100644 --- a/source/CachingSQLiteDatabase.cpp +++ b/source/CachingSQLiteDatabase.cpp @@ -26,6 +26,26 @@ #include #include +#if defined(_WIN32) || defined(WIN32) +extern "C" char* strptime(const char* s, + const char* f, + struct tm* tm) { + // Isn't the C++ standard lib nice? std::get_time is defined such that its + // format parameters are the exact same as strptime. Of course, we have to + // create a string stream first, and imbue it with the current C locale, and + // we also have to make sure we return the right things if it fails, or + // if it succeeds, but this is still far simpler an implementation than any + // of the versions in any of the C standard libraries. + std::istringstream input(s); + input.imbue(std::locale(setlocale(LC_ALL, nullptr))); + input >> std::get_time(tm, f); + if (input.fail()) { + return nullptr; + } + return (char*)(s + input.tellg()); +} +#endif + namespace nativeformat { namespace http {