diff options
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/aarch64-toolchain.cmake | 6 | ||||
-rw-r--r-- | src/util/all_tests.go | 240 | ||||
-rw-r--r-- | src/util/all_tests.sh | 85 | ||||
-rw-r--r-- | src/util/arm-toolchain.cmake | 6 | ||||
-rw-r--r-- | src/util/bot/DEPS | 134 | ||||
-rw-r--r-- | src/util/bot/README | 3 | ||||
-rw-r--r-- | src/util/bot/cmake-linux64.tar.gz.sha1 | 1 | ||||
-rw-r--r-- | src/util/bot/cmake-mac.tar.gz.sha1 | 1 | ||||
-rw-r--r-- | src/util/bot/cmake-win32.zip.sha1 | 1 | ||||
-rw-r--r-- | src/util/bot/extract.py | 139 | ||||
-rwxr-xr-x | src/util/bot/go/bootstrap.py | 297 | ||||
-rwxr-xr-x | src/util/bot/go/env.py | 49 | ||||
-rw-r--r-- | src/util/bot/perl-win32.zip.sha1 | 1 | ||||
-rw-r--r-- | src/util/bot/toolchain_vs2013.hash | 1 | ||||
-rw-r--r-- | src/util/bot/update_clang.py | 71 | ||||
-rw-r--r-- | src/util/bot/vs_env.py | 37 | ||||
-rw-r--r-- | src/util/bot/vs_toolchain.py | 114 | ||||
-rw-r--r-- | src/util/bot/yasm-win32.exe.sha1 | 1 | ||||
-rw-r--r-- | src/util/clang-toolchain.cmake | 2 | ||||
-rw-r--r-- | src/util/doc.config | 8 | ||||
-rw-r--r-- | src/util/doc.go | 21 | ||||
-rw-r--r-- | src/util/generate_build_files.py | 341 | ||||
-rw-r--r-- | src/util/make_errors.go | 106 |
23 files changed, 1502 insertions, 163 deletions
diff --git a/src/util/aarch64-toolchain.cmake b/src/util/aarch64-toolchain.cmake deleted file mode 100644 index 77f33ab..0000000 --- a/src/util/aarch64-toolchain.cmake +++ /dev/null @@ -1,6 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_SYSTEM_PROCESSOR "aarch64") -set(CMAKE_CXX_COMPILER "/opt/gcc-linaro-4.9-2014.11-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++") -set(CMAKE_C_COMPILER "/opt/gcc-linaro-4.9-2014.11-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc") -set(CMAKE_EXE_LINKER_FLAGS "-static") diff --git a/src/util/all_tests.go b/src/util/all_tests.go new file mode 100644 index 0000000..91822d1 --- /dev/null +++ b/src/util/all_tests.go @@ -0,0 +1,240 @@ +/* Copyright (c) 2015, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "os" + "os/exec" + "path" + "strings" + "time" +) + +// TODO(davidben): Link tests with the malloc shim and port -malloc-test to this runner. + +var ( + useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind") + buildDir = flag.String("build-dir", "build", "The build directory to run the tests from.") + jsonOutput = flag.String("json-output", "", "The file to output JSON results to.") +) + +type test []string + +var tests = []test{ + {"crypto/base64/base64_test"}, + {"crypto/bio/bio_test"}, + {"crypto/bn/bn_test"}, + {"crypto/bytestring/bytestring_test"}, + {"crypto/cipher/aead_test", "aes-128-gcm", "crypto/cipher/test/aes_128_gcm_tests.txt"}, + {"crypto/cipher/aead_test", "aes-128-key-wrap", "crypto/cipher/test/aes_128_key_wrap_tests.txt"}, + {"crypto/cipher/aead_test", "aes-256-gcm", "crypto/cipher/test/aes_256_gcm_tests.txt"}, + {"crypto/cipher/aead_test", "aes-256-key-wrap", "crypto/cipher/test/aes_256_key_wrap_tests.txt"}, + {"crypto/cipher/aead_test", "chacha20-poly1305", "crypto/cipher/test/chacha20_poly1305_tests.txt"}, + {"crypto/cipher/aead_test", "rc4-md5-tls", "crypto/cipher/test/rc4_md5_tls_tests.txt"}, + {"crypto/cipher/aead_test", "rc4-sha1-tls", "crypto/cipher/test/rc4_sha1_tls_tests.txt"}, + {"crypto/cipher/aead_test", "aes-128-cbc-sha1-tls", "crypto/cipher/test/aes_128_cbc_sha1_tls_tests.txt"}, + {"crypto/cipher/aead_test", "aes-128-cbc-sha1-tls-implicit-iv", "crypto/cipher/test/aes_128_cbc_sha1_tls_implicit_iv_tests.txt"}, + {"crypto/cipher/aead_test", "aes-128-cbc-sha256-tls", "crypto/cipher/test/aes_128_cbc_sha256_tls_tests.txt"}, + {"crypto/cipher/aead_test", "aes-256-cbc-sha1-tls", "crypto/cipher/test/aes_256_cbc_sha1_tls_tests.txt"}, + {"crypto/cipher/aead_test", "aes-256-cbc-sha1-tls-implicit-iv", "crypto/cipher/test/aes_256_cbc_sha1_tls_implicit_iv_tests.txt"}, + {"crypto/cipher/aead_test", "aes-256-cbc-sha256-tls", "crypto/cipher/test/aes_256_cbc_sha256_tls_tests.txt"}, + {"crypto/cipher/aead_test", "aes-256-cbc-sha384-tls", "crypto/cipher/test/aes_256_cbc_sha384_tls_tests.txt"}, + {"crypto/cipher/aead_test", "des-ede3-cbc-sha1-tls", "crypto/cipher/test/des_ede3_cbc_sha1_tls_tests.txt"}, + {"crypto/cipher/aead_test", "des-ede3-cbc-sha1-tls-implicit-iv", "crypto/cipher/test/des_ede3_cbc_sha1_tls_implicit_iv_tests.txt"}, + {"crypto/cipher/aead_test", "rc4-md5-ssl3", "crypto/cipher/test/rc4_md5_ssl3_tests.txt"}, + {"crypto/cipher/aead_test", "rc4-sha1-ssl3", "crypto/cipher/test/rc4_sha1_ssl3_tests.txt"}, + {"crypto/cipher/aead_test", "aes-128-cbc-sha1-ssl3", "crypto/cipher/test/aes_128_cbc_sha1_ssl3_tests.txt"}, + {"crypto/cipher/aead_test", "aes-256-cbc-sha1-ssl3", "crypto/cipher/test/aes_256_cbc_sha1_ssl3_tests.txt"}, + {"crypto/cipher/aead_test", "des-ede3-cbc-sha1-ssl3", "crypto/cipher/test/des_ede3_cbc_sha1_ssl3_tests.txt"}, + {"crypto/cipher/aead_test", "aes-128-ctr-hmac-sha256", "crypto/cipher/test/aes_128_ctr_hmac_sha256.txt"}, + {"crypto/cipher/aead_test", "aes-256-ctr-hmac-sha256", "crypto/cipher/test/aes_256_ctr_hmac_sha256.txt"}, + {"crypto/cipher/cipher_test", "crypto/cipher/test/cipher_test.txt"}, + {"crypto/cmac/cmac_test"}, + {"crypto/constant_time_test"}, + {"crypto/dh/dh_test"}, + {"crypto/digest/digest_test"}, + {"crypto/dsa/dsa_test"}, + {"crypto/ec/ec_test"}, + {"crypto/ec/example_mul"}, + {"crypto/ecdsa/ecdsa_test"}, + {"crypto/err/err_test"}, + {"crypto/evp/evp_extra_test"}, + {"crypto/evp/evp_test", "crypto/evp/evp_tests.txt"}, + {"crypto/evp/evp_test", "crypto/hmac/hmac_tests.txt"}, + {"crypto/evp/pbkdf_test"}, + {"crypto/hkdf/hkdf_test"}, + {"crypto/hmac/hmac_test", "crypto/hmac/hmac_tests.txt"}, + {"crypto/lhash/lhash_test"}, + {"crypto/modes/gcm_test"}, + {"crypto/pkcs8/pkcs12_test"}, + {"crypto/rsa/rsa_test"}, + {"crypto/thread_test"}, + {"crypto/x509/pkcs7_test"}, + {"crypto/x509v3/tab_test"}, + {"crypto/x509v3/v3name_test"}, + {"ssl/pqueue/pqueue_test"}, + {"ssl/ssl_test"}, +} + +// testOutput is a representation of Chromium's JSON test result format. See +// https://www.chromium.org/developers/the-json-test-results-format +type testOutput struct { + Version int `json:"version"` + Interrupted bool `json:"interrupted"` + PathDelimiter string `json:"path_delimiter"` + SecondsSinceEpoch float64 `json:"seconds_since_epoch"` + NumFailuresByType map[string]int `json:"num_failures_by_type"` + Tests map[string]testResult `json:"tests"` +} + +type testResult struct { + Actual string `json:"actual"` + Expected string `json:"expected"` + IsUnexpected bool `json:"is_unexpected"` +} + +func newTestOutput() *testOutput { + return &testOutput{ + Version: 3, + PathDelimiter: ".", + SecondsSinceEpoch: float64(time.Now().UnixNano()) / float64(time.Second/time.Nanosecond), + NumFailuresByType: make(map[string]int), + Tests: make(map[string]testResult), + } +} + +func (t *testOutput) addResult(name, result string) { + if _, found := t.Tests[name]; found { + panic(name) + } + t.Tests[name] = testResult{ + Actual: result, + Expected: "PASS", + IsUnexpected: result != "PASS", + } + t.NumFailuresByType[result]++ +} + +func (t *testOutput) writeTo(name string) error { + file, err := os.Create(name) + if err != nil { + return err + } + defer file.Close() + out, err := json.MarshalIndent(t, "", " ") + if err != nil { + return err + } + _, err = file.Write(out) + return err +} + +func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd { + valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full"} + if dbAttach { + valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p") + } + valgrindArgs = append(valgrindArgs, path) + valgrindArgs = append(valgrindArgs, args...) + + return exec.Command("valgrind", valgrindArgs...) +} + +func runTest(test test) (passed bool, err error) { + prog := path.Join(*buildDir, test[0]) + args := test[1:] + var cmd *exec.Cmd + if *useValgrind { + cmd = valgrindOf(false, prog, args...) + } else { + cmd = exec.Command(prog, args...) + } + var stdoutBuf bytes.Buffer + cmd.Stdout = &stdoutBuf + cmd.Stderr = os.Stderr + + if err := cmd.Start(); err != nil { + return false, err + } + if err := cmd.Wait(); err != nil { + return false, err + } + + // Account for Windows line-endings. + stdout := bytes.Replace(stdoutBuf.Bytes(), []byte("\r\n"), []byte("\n"), -1) + + if bytes.HasSuffix(stdout, []byte("PASS\n")) && + (len(stdout) == 5 || stdout[len(stdout)-6] == '\n') { + return true, nil + } + return false, nil +} + +// shortTestName returns the short name of a test. Except for evp_test, it +// assumes that any argument which ends in .txt is a path to a data file and not +// relevant to the test's uniqueness. +func shortTestName(test test) string { + var args []string + for _, arg := range test { + if test[0] == "crypto/evp/evp_test" || !strings.HasSuffix(arg, ".txt") { + args = append(args, arg) + } + } + return strings.Join(args, " ") +} + +func main() { + flag.Parse() + + testOutput := newTestOutput() + var failed []test + for _, test := range tests { + fmt.Printf("%s\n", strings.Join([]string(test), " ")) + + name := shortTestName(test) + passed, err := runTest(test) + if err != nil { + fmt.Printf("%s failed to complete: %s\n", test[0], err) + failed = append(failed, test) + testOutput.addResult(name, "CRASHED") + } else if !passed { + fmt.Printf("%s failed to print PASS on the last line.\n", test[0]) + failed = append(failed, test) + testOutput.addResult(name, "FAIL") + } else { + testOutput.addResult(name, "PASS") + } + } + + if *jsonOutput != "" { + if err := testOutput.writeTo(*jsonOutput); err != nil { + fmt.Fprintf(os.Stderr, "Error: %s\n", err) + } + } + + if len(failed) > 0 { + fmt.Printf("\n%d of %d tests failed:\n", len(failed), len(tests)) + for _, test := range failed { + fmt.Printf("\t%s\n", strings.Join([]string(test), " ")) + } + os.Exit(1) + } + + fmt.Printf("\nAll tests passed!\n") +} diff --git a/src/util/all_tests.sh b/src/util/all_tests.sh deleted file mode 100644 index bcb5c24..0000000 --- a/src/util/all_tests.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2014, Google Inc. -# -# Permission to use, copy, modify, and/or distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION -# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -SRC=.. -if [ "$#" -ge 1 ]; then - SRC=$1 -fi - -TESTS=" -./crypto/base64/base64_test -./crypto/bio/bio_test -./crypto/bn/bn_test -./crypto/bytestring/bytestring_test -./crypto/cipher/aead_test aes-128-gcm $SRC/crypto/cipher/test/aes_128_gcm_tests.txt -./crypto/cipher/aead_test aes-128-key-wrap $SRC/crypto/cipher/test/aes_128_key_wrap_tests.txt -./crypto/cipher/aead_test aes-256-gcm $SRC/crypto/cipher/test/aes_256_gcm_tests.txt -./crypto/cipher/aead_test aes-256-key-wrap $SRC/crypto/cipher/test/aes_256_key_wrap_tests.txt -./crypto/cipher/aead_test chacha20-poly1305 $SRC/crypto/cipher/test/chacha20_poly1305_tests.txt -./crypto/cipher/aead_test rc4-md5-tls $SRC/crypto/cipher/test/rc4_md5_tls_tests.txt -./crypto/cipher/aead_test rc4-sha1-tls $SRC/crypto/cipher/test/rc4_sha1_tls_tests.txt -./crypto/cipher/aead_test aes-128-cbc-sha1-tls $SRC/crypto/cipher/test/aes_128_cbc_sha1_tls_tests.txt -./crypto/cipher/aead_test aes-128-cbc-sha1-tls-implicit-iv $SRC/crypto/cipher/test/aes_128_cbc_sha1_tls_implicit_iv_tests.txt -./crypto/cipher/aead_test aes-128-cbc-sha256-tls $SRC/crypto/cipher/test/aes_128_cbc_sha256_tls_tests.txt -./crypto/cipher/aead_test aes-256-cbc-sha1-tls $SRC/crypto/cipher/test/aes_256_cbc_sha1_tls_tests.txt -./crypto/cipher/aead_test aes-256-cbc-sha1-tls-implicit-iv $SRC/crypto/cipher/test/aes_256_cbc_sha1_tls_implicit_iv_tests.txt -./crypto/cipher/aead_test aes-256-cbc-sha256-tls $SRC/crypto/cipher/test/aes_256_cbc_sha256_tls_tests.txt -./crypto/cipher/aead_test aes-256-cbc-sha384-tls $SRC/crypto/cipher/test/aes_256_cbc_sha384_tls_tests.txt -./crypto/cipher/aead_test des-ede3-cbc-sha1-tls $SRC/crypto/cipher/test/des_ede3_cbc_sha1_tls_tests.txt -./crypto/cipher/aead_test des-ede3-cbc-sha1-tls-implicit-iv $SRC/crypto/cipher/test/des_ede3_cbc_sha1_tls_implicit_iv_tests.txt -./crypto/cipher/aead_test rc4-md5-ssl3 $SRC/crypto/cipher/test/rc4_md5_ssl3_tests.txt -./crypto/cipher/aead_test rc4-sha1-ssl3 $SRC/crypto/cipher/test/rc4_sha1_ssl3_tests.txt -./crypto/cipher/aead_test aes-128-cbc-sha1-ssl3 $SRC/crypto/cipher/test/aes_128_cbc_sha1_ssl3_tests.txt -./crypto/cipher/aead_test aes-256-cbc-sha1-ssl3 $SRC/crypto/cipher/test/aes_256_cbc_sha1_ssl3_tests.txt -./crypto/cipher/aead_test des-ede3-cbc-sha1-ssl3 $SRC/crypto/cipher/test/des_ede3_cbc_sha1_ssl3_tests.txt -./crypto/cipher/cipher_test $SRC/crypto/cipher/test/cipher_test.txt -./crypto/constant_time_test -./crypto/dh/dh_test -./crypto/digest/digest_test -./crypto/dsa/dsa_test -./crypto/ec/ec_test -./crypto/ec/example_mul -./crypto/ecdsa/ecdsa_test -./crypto/err/err_test -./crypto/evp/evp_test -./crypto/evp/pbkdf_test -./crypto/hkdf/hkdf_test -./crypto/hmac/hmac_test -./crypto/lhash/lhash_test -./crypto/modes/gcm_test -./crypto/pkcs8/pkcs12_test -./crypto/rsa/rsa_test -./crypto/x509/pkcs7_test -./crypto/x509v3/tab_test -./crypto/x509v3/v3name_test -./ssl/pqueue/pqueue_test -./ssl/ssl_test -" - -IFS=$'\n' -for bin in $TESTS; do - echo $bin - out=$(bash -c "$bin" | tail -n 1) - if [ $? -ne 0 ]; then - echo $bin failed to complete. - exit 1 - fi - - if [ "x$out" != "xPASS" ]; then - echo $bin failed to print PASS on the last line. - exit 1 - fi -done diff --git a/src/util/arm-toolchain.cmake b/src/util/arm-toolchain.cmake deleted file mode 100644 index 2dfd2bd..0000000 --- a/src/util/arm-toolchain.cmake +++ /dev/null @@ -1,6 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_SYSTEM_PROCESSOR "arm") -set(CMAKE_CXX_COMPILER "/opt/gcc-linaro-4.9-2014.11-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++") -set(CMAKE_C_COMPILER "/opt/gcc-linaro-4.9-2014.11-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc") -set(CMAKE_EXE_LINKER_FLAGS "-static") diff --git a/src/util/bot/DEPS b/src/util/bot/DEPS new file mode 100644 index 0000000..738fbd3 --- /dev/null +++ b/src/util/bot/DEPS @@ -0,0 +1,134 @@ +# Copyright (c) 2015, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +vars = { + 'chromium_git': 'https://chromium.googlesource.com', +} + +deps = { + 'boringssl/util/bot/gyp': + Var('chromium_git') + '/external/gyp.git' + '@' + '4a9b712d5cb4a5ba7a9950128a7219569caf7263', +} + +hooks = [ + { + 'name': 'cmake_linux64', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', 'chromium-tools', + '-s', 'boringssl/util/bot/cmake-linux64.tar.gz.sha1', + ], + }, + { + 'name': 'cmake_mac', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=darwin', + '--no_auth', + '--bucket', 'chromium-tools', + '-s', 'boringssl/util/bot/cmake-mac.tar.gz.sha1', + ], + }, + { + 'name': 'cmake_win32', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=win32', + '--no_auth', + '--bucket', 'chromium-tools', + '-s', 'boringssl/util/bot/cmake-win32.zip.sha1', + ], + }, + { + 'name': 'perl_win32', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=win32', + '--no_auth', + '--bucket', 'chromium-tools', + '-s', 'boringssl/util/bot/perl-win32.zip.sha1', + ], + }, + { + 'name': 'yasm_win32', + 'pattern': '.', + 'action': [ 'download_from_google_storage', + '--no_resume', + '--platform=win32', + '--no_auth', + '--bucket', 'chromium-tools', + '-s', 'boringssl/util/bot/yasm-win32.exe.sha1', + ], + }, + { + 'name': 'win_toolchain', + 'pattern': '.', + 'action': [ 'python', + 'boringssl/util/bot/vs_toolchain.py', + 'update', + ], + }, + { + 'name': 'clang', + 'pattern': '.', + 'action': [ 'python', + 'boringssl/util/bot/update_clang.py', + ], + }, + # TODO(davidben): Only extract archives when they've changed. Extracting perl + # on Windows is a significant part of the cycle time. + { + 'name': 'cmake_linux64_extract', + 'pattern': '.', + 'action': [ 'python', + 'boringssl/util/bot/extract.py', + 'boringssl/util/bot/cmake-linux64.tar.gz', + 'boringssl/util/bot/cmake-linux64/', + ], + }, + { + 'name': 'cmake_mac_extract', + 'pattern': '.', + 'action': [ 'python', + 'boringssl/util/bot/extract.py', + 'boringssl/util/bot/cmake-mac.tar.gz', + 'boringssl/util/bot/cmake-mac/', + ], + }, + { + 'name': 'cmake_win32_extract', + 'pattern': '.', + 'action': [ 'python', + 'boringssl/util/bot/extract.py', + 'boringssl/util/bot/cmake-win32.zip', + 'boringssl/util/bot/cmake-win32/', + ], + }, + { + 'name': 'perl_win32_extract', + 'pattern': '.', + 'action': [ 'python', + 'boringssl/util/bot/extract.py', + '--no-prefix', + 'boringssl/util/bot/perl-win32.zip', + 'boringssl/util/bot/perl-win32/', + ], + }, +] diff --git a/src/util/bot/README b/src/util/bot/README new file mode 100644 index 0000000..b7a4332 --- /dev/null +++ b/src/util/bot/README @@ -0,0 +1,3 @@ +This directory contains tools for setting up a hermetic toolchain on the +continuous integration bots. It is in the repository for convenience and can be +ignored in development. diff --git a/src/util/bot/cmake-linux64.tar.gz.sha1 b/src/util/bot/cmake-linux64.tar.gz.sha1 new file mode 100644 index 0000000..6a8aa1c --- /dev/null +++ b/src/util/bot/cmake-linux64.tar.gz.sha1 @@ -0,0 +1 @@ +32cd1d5fe84ae569dbb36f5767650d62efb8be38
\ No newline at end of file diff --git a/src/util/bot/cmake-mac.tar.gz.sha1 b/src/util/bot/cmake-mac.tar.gz.sha1 new file mode 100644 index 0000000..cb7251b --- /dev/null +++ b/src/util/bot/cmake-mac.tar.gz.sha1 @@ -0,0 +1 @@ +310df6945ae7f8c9da559d22f5794ee8e578a663
\ No newline at end of file diff --git a/src/util/bot/cmake-win32.zip.sha1 b/src/util/bot/cmake-win32.zip.sha1 new file mode 100644 index 0000000..9196b58 --- /dev/null +++ b/src/util/bot/cmake-win32.zip.sha1 @@ -0,0 +1 @@ +e9493171de0edd8879755aa7229a701010a19561
\ No newline at end of file diff --git a/src/util/bot/extract.py b/src/util/bot/extract.py new file mode 100644 index 0000000..77603c0 --- /dev/null +++ b/src/util/bot/extract.py @@ -0,0 +1,139 @@ +# Copyright (c) 2015, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Extracts archives.""" + + +import optparse +import os +import os.path +import tarfile +import shutil +import sys +import zipfile + + +def CheckedJoin(output, path): + """ + CheckedJoin returns os.path.join(output, path). It does sanity checks to + ensure the resulting path is under output, but shouldn't be used on untrusted + input. + """ + path = os.path.normpath(path) + if os.path.isabs(path) or path.startswith('.'): + raise ValueError(path) + return os.path.join(output, path) + + +def IterateZip(path): + """ + IterateZip opens the zip file at path and returns a generator of + (filename, mode, fileobj) tuples for each file in it. + """ + with zipfile.ZipFile(path, 'r') as zip_file: + for info in zip_file.infolist(): + if info.filename.endswith('/'): + continue + yield (info.filename, None, zip_file.open(info)) + + +def IterateTar(path): + """ + IterateTar opens the tar.gz file at path and returns a generator of + (filename, mode, fileobj) tuples for each file in it. + """ + with tarfile.open(path, 'r:gz') as tar_file: + for info in tar_file: + if info.isdir(): + continue + if not info.isfile(): + raise ValueError('Unknown entry type "%s"' % (info.name, )) + yield (info.name, info.mode, tar_file.extractfile(info)) + + +def main(args): + parser = optparse.OptionParser(usage='Usage: %prog ARCHIVE OUTPUT') + parser.add_option('--no-prefix', dest='no_prefix', action='store_true', + help='Do not remove a prefix from paths in the archive.') + options, args = parser.parse_args(args) + + if len(args) != 2: + parser.print_help() + return 1 + + archive, output = args + + if not os.path.exists(archive): + # Skip archives that weren't downloaded. + return 0 + + if archive.endswith('.zip'): + entries = IterateZip(archive) + elif archive.endswith('.tar.gz'): + entries = IterateTar(archive) + else: + raise ValueError(archive) + + try: + if os.path.exists(output): + print "Removing %s" % (output, ) + shutil.rmtree(output) + + print "Extracting %s to %s" % (archive, output) + prefix = None + num_extracted = 0 + for path, mode, inp in entries: + # Even on Windows, zip files must always use forward slashes. + if '\\' in path or path.startswith('/'): + raise ValueError(path) + + if not options.no_prefix: + new_prefix, rest = path.split('/', 1) + + # Ensure the archive is consistent. + if prefix is None: + prefix = new_prefix + if prefix != new_prefix: + raise ValueError((prefix, new_prefix)) + else: + rest = path + + # Extract the file into the output directory. + fixed_path = CheckedJoin(output, rest) + if not os.path.isdir(os.path.dirname(fixed_path)): + os.makedirs(os.path.dirname(fixed_path)) + with open(fixed_path, 'wb') as out: + shutil.copyfileobj(inp, out) + + # Fix up permissions if needbe. + # TODO(davidben): To be extra tidy, this should only track the execute bit + # as in git. + if mode is not None: + os.chmod(fixed_path, mode) + + # Print every 100 files, so bots do not time out on large archives. + num_extracted += 1 + if num_extracted % 100 == 0: + print "Extracted %d files..." % (num_extracted,) + finally: + entries.close() + + if num_extracted % 100 == 0: + print "Done. Extracted %d files." % (num_extracted,) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/src/util/bot/go/bootstrap.py b/src/util/bot/go/bootstrap.py new file mode 100755 index 0000000..166ef3b --- /dev/null +++ b/src/util/bot/go/bootstrap.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Modified from go/bootstrap.py in Chromium infrastructure's repository to patch +# out everything but the core toolchain. +# +# https://chromium.googlesource.com/infra/infra/ + +"""Prepares a local hermetic Go installation. + +- Downloads and unpacks the Go toolset in ../golang. +""" + +import contextlib +import logging +import os +import platform +import shutil +import stat +import subprocess +import sys +import tarfile +import tempfile +import urllib +import zipfile + +# TODO(vadimsh): Migrate to new golang.org/x/ paths once Golang moves to +# git completely. + +LOGGER = logging.getLogger(__name__) + + +# /path/to/util/bot +ROOT = os.path.dirname(os.path.abspath(__file__)) + +# Where to install Go toolset to. GOROOT would be <TOOLSET_ROOT>/go. +TOOLSET_ROOT = os.path.join(os.path.dirname(ROOT), 'golang') + +# Default workspace with infra go code. +WORKSPACE = os.path.join(ROOT, 'go') + +# Platform depended suffix for executable files. +EXE_SFX = '.exe' if sys.platform == 'win32' else '' + +# Pinned version of Go toolset to download. +TOOLSET_VERSION = 'go1.4' + +# Platform dependent portion of a download URL. See http://golang.org/dl/. +TOOLSET_VARIANTS = { + ('darwin', 'x86-32'): 'darwin-386-osx10.8.tar.gz', + ('darwin', 'x86-64'): 'darwin-amd64-osx10.8.tar.gz', + ('linux2', 'x86-32'): 'linux-386.tar.gz', + ('linux2', 'x86-64'): 'linux-amd64.tar.gz', + ('win32', 'x86-32'): 'windows-386.zip', + ('win32', 'x86-64'): 'windows-amd64.zip', +} + +# Download URL root. +DOWNLOAD_URL_PREFIX = 'https://storage.googleapis.com/golang' + + +class Failure(Exception): + """Bootstrap failed.""" + + +def get_toolset_url(): + """URL of a platform specific Go toolset archive.""" + # TODO(vadimsh): Support toolset for cross-compilation. + arch = { + 'amd64': 'x86-64', + 'x86_64': 'x86-64', + 'i386': 'x86-32', + 'x86': 'x86-32', + }.get(platform.machine().lower()) + variant = TOOLSET_VARIANTS.get((sys.platform, arch)) + if not variant: + # TODO(vadimsh): Compile go lang from source. + raise Failure('Unrecognized platform') + return '%s/%s.%s' % (DOWNLOAD_URL_PREFIX, TOOLSET_VERSION, variant) + + +def read_file(path): + """Returns contents of a given file or None if not readable.""" + assert isinstance(path, (list, tuple)) + try: + with open(os.path.join(*path), 'r') as f: + return f.read() + except IOError: + return None + + +def write_file(path, data): + """Writes |data| to a file.""" + assert isinstance(path, (list, tuple)) + with open(os.path.join(*path), 'w') as f: + f.write(data) + + +def remove_directory(path): + """Recursively removes a directory.""" + assert isinstance(path, (list, tuple)) + p = os.path.join(*path) + if not os.path.exists(p): + return + LOGGER.info('Removing %s', p) + # Crutch to remove read-only file (.git/* in particular) on Windows. + def onerror(func, path, _exc_info): + if not os.access(path, os.W_OK): + os.chmod(path, stat.S_IWUSR) + func(path) + else: + raise + shutil.rmtree(p, onerror=onerror if sys.platform == 'win32' else None) + + +def install_toolset(toolset_root, url): + """Downloads and installs Go toolset. + + GOROOT would be <toolset_root>/go/. + """ + if not os.path.exists(toolset_root): + os.makedirs(toolset_root) + pkg_path = os.path.join(toolset_root, url[url.rfind('/')+1:]) + + LOGGER.info('Downloading %s...', url) + download_file(url, pkg_path) + + LOGGER.info('Extracting...') + if pkg_path.endswith('.zip'): + with zipfile.ZipFile(pkg_path, 'r') as f: + f.extractall(toolset_root) + elif pkg_path.endswith('.tar.gz'): + with tarfile.open(pkg_path, 'r:gz') as f: + f.extractall(toolset_root) + else: + raise Failure('Unrecognized archive format') + + LOGGER.info('Validating...') + if not check_hello_world(toolset_root): + raise Failure('Something is not right, test program doesn\'t work') + + +def download_file(url, path): + """Fetches |url| to |path|.""" + last_progress = [0] + def report(a, b, c): + progress = int(a * b * 100.0 / c) + if progress != last_progress[0]: + print >> sys.stderr, 'Downloading... %d%%' % progress + last_progress[0] = progress + # TODO(vadimsh): Use something less crippled, something that validates SSL. + urllib.urlretrieve(url, path, reporthook=report) + + +@contextlib.contextmanager +def temp_dir(path): + """Creates a temporary directory, then deletes it.""" + tmp = tempfile.mkdtemp(dir=path) + try: + yield tmp + finally: + remove_directory([tmp]) + + +def check_hello_world(toolset_root): + """Compiles and runs 'hello world' program to verify that toolset works.""" + with temp_dir(toolset_root) as tmp: + path = os.path.join(tmp, 'hello.go') + write_file([path], r""" + package main + func main() { println("hello, world\n") } + """) + out = subprocess.check_output( + [get_go_exe(toolset_root), 'run', path], + env=get_go_environ(toolset_root, tmp), + stderr=subprocess.STDOUT) + if out.strip() != 'hello, world': + LOGGER.error('Failed to run sample program:\n%s', out) + return False + return True + + +def ensure_toolset_installed(toolset_root): + """Installs or updates Go toolset if necessary. + + Returns True if new toolset was installed. + """ + installed = read_file([toolset_root, 'INSTALLED_TOOLSET']) + available = get_toolset_url() + if installed == available: + LOGGER.debug('Go toolset is up-to-date: %s', TOOLSET_VERSION) + return False + + LOGGER.info('Installing Go toolset.') + LOGGER.info(' Old toolset is %s', installed) + LOGGER.info(' New toolset is %s', available) + remove_directory([toolset_root]) + install_toolset(toolset_root, available) + LOGGER.info('Go toolset installed: %s', TOOLSET_VERSION) + write_file([toolset_root, 'INSTALLED_TOOLSET'], available) + return True + + +def get_go_environ( + toolset_root, + workspace=None): + """Returns a copy of os.environ with added GO* environment variables. + + Overrides GOROOT, GOPATH and GOBIN. Keeps everything else. Idempotent. + + Args: + toolset_root: GOROOT would be <toolset_root>/go. + workspace: main workspace directory or None if compiling in GOROOT. + """ + env = os.environ.copy() + env['GOROOT'] = os.path.join(toolset_root, 'go') + if workspace: + env['GOBIN'] = os.path.join(workspace, 'bin') + else: + env.pop('GOBIN', None) + + all_go_paths = [] + if workspace: + all_go_paths.append(workspace) + env['GOPATH'] = os.pathsep.join(all_go_paths) + + # New PATH entries. + paths_to_add = [ + os.path.join(env['GOROOT'], 'bin'), + env.get('GOBIN'), + ] + + # Make sure not to add duplicates entries to PATH over and over again when + # get_go_environ is invoked multiple times. + path = env['PATH'].split(os.pathsep) + paths_to_add = [p for p in paths_to_add if p and p not in path] + env['PATH'] = os.pathsep.join(paths_to_add + path) + + return env + + +def get_go_exe(toolset_root): + """Returns path to go executable.""" + return os.path.join(toolset_root, 'go', 'bin', 'go' + EXE_SFX) + + +def bootstrap(logging_level): + """Installs all dependencies in default locations. + + Supposed to be called at the beginning of some script (it modifies logger). + + Args: + logging_level: logging level of bootstrap process. + """ + logging.basicConfig() + LOGGER.setLevel(logging_level) + ensure_toolset_installed(TOOLSET_ROOT) + + +def prepare_go_environ(): + """Returns dict with environment variables to set to use Go toolset. + + Installs or updates the toolset if necessary. + """ + bootstrap(logging.INFO) + return get_go_environ(TOOLSET_ROOT, WORKSPACE) + + +def find_executable(name, workspaces): + """Returns full path to an executable in some bin/ (in GOROOT or GOBIN).""" + basename = name + if EXE_SFX and basename.endswith(EXE_SFX): + basename = basename[:-len(EXE_SFX)] + roots = [os.path.join(TOOLSET_ROOT, 'go', 'bin')] + for path in workspaces: + roots.extend([ + os.path.join(path, 'bin'), + ]) + for root in roots: + full_path = os.path.join(root, basename + EXE_SFX) + if os.path.exists(full_path): + return full_path + return name + + +def main(args): + if args: + print >> sys.stderr, sys.modules[__name__].__doc__, + return 2 + bootstrap(logging.DEBUG) + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/src/util/bot/go/env.py b/src/util/bot/go/env.py new file mode 100755 index 0000000..820968c --- /dev/null +++ b/src/util/bot/go/env.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Modified from go/env.py in Chromium infrastructure's repository to patch out +# everything but the core toolchain. +# +# https://chromium.googlesource.com/infra/infra/ + +"""Can be used to point environment variable to hermetic Go toolset. + +Usage (on linux and mac): +$ eval `./env.py` +$ go version + +Or it can be used to wrap a command: + +$ ./env.py go version +""" + +assert __name__ == '__main__' + +import imp +import os +import subprocess +import sys + +# Do not want to mess with sys.path, load the module directly. +bootstrap = imp.load_source( + 'bootstrap', os.path.join(os.path.dirname(__file__), 'bootstrap.py')) + +old = os.environ.copy() +new = bootstrap.prepare_go_environ() + +if len(sys.argv) == 1: + for key, value in sorted(new.iteritems()): + if old.get(key) != value: + print 'export %s="%s"' % (key, value) +else: + exe = sys.argv[1] + if exe == 'python': + exe = sys.executable + else: + # Help Windows to find the executable in new PATH, do it only when + # executable is referenced by name (and not by path). + if os.sep not in exe: + exe = bootstrap.find_executable(exe, [bootstrap.WORKSPACE]) + sys.exit(subprocess.call([exe] + sys.argv[2:], env=new)) diff --git a/src/util/bot/perl-win32.zip.sha1 b/src/util/bot/perl-win32.zip.sha1 new file mode 100644 index 0000000..a5559d8 --- /dev/null +++ b/src/util/bot/perl-win32.zip.sha1 @@ -0,0 +1 @@ +ab6e7aee6a915c4d820b86f5227094763b649fce
\ No newline at end of file diff --git a/src/util/bot/toolchain_vs2013.hash b/src/util/bot/toolchain_vs2013.hash new file mode 100644 index 0000000..4ed8816 --- /dev/null +++ b/src/util/bot/toolchain_vs2013.hash @@ -0,0 +1 @@ +ee7d718ec60c2dc5d255bbe325909c2021a7efef diff --git a/src/util/bot/update_clang.py b/src/util/bot/update_clang.py new file mode 100644 index 0000000..0836d11 --- /dev/null +++ b/src/util/bot/update_clang.py @@ -0,0 +1,71 @@ +# Copyright (c) 2015, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import os.path +import shutil +import sys +import tarfile +import tempfile +import urllib + +# CLANG_REVISION and CLANG_SUB_REVISION determine the build of clang +# to use. These should be synced with tools/clang/scripts/update.sh in +# Chromium. +CLANG_REVISION = "233105" +CLANG_SUB_REVISION = "1" + +PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION) +LLVM_BUILD_DIR = os.path.join(os.path.dirname(__file__), "llvm-build") +STAMP_FILE = os.path.join(LLVM_BUILD_DIR, "cr_build_revision") + +CDS_URL = "https://commondatastorage.googleapis.com/chromium-browser-clang" + +def DownloadFile(url, path): + """DownloadFile fetches |url| to |path|.""" + last_progress = [0] + def report(a, b, c): + progress = int(a * b * 100.0 / c) + if progress != last_progress[0]: + print >> sys.stderr, "Downloading... %d%%" % progress + last_progress[0] = progress + urllib.urlretrieve(url, path, reporthook=report) + +def main(args): + # For now, only download clang on Linux. + if not sys.platform.startswith("linux"): + return 0 + + if os.path.exists(STAMP_FILE): + with open(STAMP_FILE) as f: + if f.read().strip() == PACKAGE_VERSION: + print >> sys.stderr, "Clang already at %s" % (PACKAGE_VERSION,) + return 0 + + if os.path.exists(LLVM_BUILD_DIR): + shutil.rmtree(LLVM_BUILD_DIR) + + print >> sys.stderr, "Downloading Clang %s" % (PACKAGE_VERSION,) + cds_full_url = "%s/Linux_x64/clang-%s.tgz" % (CDS_URL, PACKAGE_VERSION) + with tempfile.NamedTemporaryFile() as temp: + DownloadFile(cds_full_url, temp.name) + with tarfile.open(temp.name, "r:gz") as tar_file: + tar_file.extractall(LLVM_BUILD_DIR) + + with open(STAMP_FILE, "wb") as stamp_file: + stamp_file.write(PACKAGE_VERSION) + + return 0 + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/src/util/bot/vs_env.py b/src/util/bot/vs_env.py new file mode 100644 index 0000000..1847500 --- /dev/null +++ b/src/util/bot/vs_env.py @@ -0,0 +1,37 @@ +# Copyright (c) 2015, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +import subprocess +import sys + +import vs_toolchain +# vs_toolchain adds gyp to sys.path. +import gyp.MSVSVersion + +if len(sys.argv) < 2: + print >>sys.stderr, "Usage: vs_env.py TARGET_ARCH CMD..." + sys.exit(1) + +target_arch = sys.argv[1] +cmd = sys.argv[2:] + +vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs() +vs_version = gyp.MSVSVersion.SelectVisualStudioVersion() + +# Using shell=True is somewhat ugly, but the alternative is to pull in a copy +# of the Chromium GN build's setup_toolchain.py which runs the setup script, +# then 'set', and then parses the environment variables out. (GYP internally +# does the same thing.) +sys.exit(subprocess.call(vs_version.SetupScript(target_arch) + ["&&"] + cmd, + shell=True)) diff --git a/src/util/bot/vs_toolchain.py b/src/util/bot/vs_toolchain.py new file mode 100644 index 0000000..fd76f39 --- /dev/null +++ b/src/util/bot/vs_toolchain.py @@ -0,0 +1,114 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import json +import os +import pipes +import shutil +import subprocess +import sys + + +script_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, os.path.join(script_dir, 'gyp', 'pylib')) +json_data_file = os.path.join(script_dir, 'win_toolchain.json') + + +import gyp + + +def SetEnvironmentAndGetRuntimeDllDirs(): + """Sets up os.environ to use the depot_tools VS toolchain with gyp, and + returns the location of the VS runtime DLLs so they can be copied into + the output directory after gyp generation. + """ + vs2013_runtime_dll_dirs = None + depot_tools_win_toolchain = \ + bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))) + if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain: + if not os.path.exists(json_data_file): + Update() + with open(json_data_file, 'r') as tempf: + toolchain_data = json.load(tempf) + + toolchain = toolchain_data['path'] + version = toolchain_data['version'] + version_is_pro = version[-1] != 'e' + win8sdk = toolchain_data['win8sdk'] + wdk = toolchain_data['wdk'] + # TODO(scottmg): The order unfortunately matters in these. They should be + # split into separate keys for x86 and x64. (See CopyVsRuntimeDlls call + # below). http://crbug.com/345992 + vs2013_runtime_dll_dirs = toolchain_data['runtime_dirs'] + + os.environ['GYP_MSVS_OVERRIDE_PATH'] = toolchain + os.environ['GYP_MSVS_VERSION'] = version + # We need to make sure windows_sdk_path is set to the automated + # toolchain values in GYP_DEFINES, but don't want to override any + # otheroptions.express + # values there. + gyp_defines_dict = gyp.NameValueListToDict(gyp.ShlexEnv('GYP_DEFINES')) + gyp_defines_dict['windows_sdk_path'] = win8sdk + os.environ['GYP_DEFINES'] = ' '.join('%s=%s' % (k, pipes.quote(str(v))) + for k, v in gyp_defines_dict.iteritems()) + os.environ['WINDOWSSDKDIR'] = win8sdk + os.environ['WDK_DIR'] = wdk + # Include the VS runtime in the PATH in case it's not machine-installed. + runtime_path = ';'.join(vs2013_runtime_dll_dirs) + os.environ['PATH'] = runtime_path + ';' + os.environ['PATH'] + return vs2013_runtime_dll_dirs + + +def _GetDesiredVsToolchainHashes(): + """Load a list of SHA1s corresponding to the toolchains that we want installed + to build with.""" + sha1path = os.path.join(script_dir, 'toolchain_vs2013.hash') + with open(sha1path, 'rb') as f: + return f.read().strip().splitlines() + + +def FindDepotTools(): + """Returns the path to depot_tools in $PATH.""" + for path in os.environ['PATH'].split(os.pathsep): + if os.path.isfile(os.path.join(path, 'gclient.py')): + return path + raise Exception("depot_tools not found!") + + +def Update(): + """Requests an update of the toolchain to the specific hashes we have at + this revision. The update outputs a .json of the various configuration + information required to pass to gyp which we use in |GetToolchainDir()|. + """ + depot_tools_win_toolchain = \ + bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))) + if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain: + depot_tools_path = FindDepotTools() + json_data_file = os.path.join(script_dir, 'win_toolchain.json') + get_toolchain_args = [ + sys.executable, + os.path.join(depot_tools_path, + 'win_toolchain', + 'get_toolchain_if_necessary.py'), + '--output-json', json_data_file, + ] + _GetDesiredVsToolchainHashes() + subprocess.check_call(get_toolchain_args) + + return 0 + + +def main(): + if not sys.platform.startswith(('win32', 'cygwin')): + return 0 + commands = { + 'update': Update, + } + if len(sys.argv) < 2 or sys.argv[1] not in commands: + print >>sys.stderr, 'Expected one of: %s' % ', '.join(commands) + return 1 + return commands[sys.argv[1]](*sys.argv[2:]) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/util/bot/yasm-win32.exe.sha1 b/src/util/bot/yasm-win32.exe.sha1 new file mode 100644 index 0000000..5b8c9aa --- /dev/null +++ b/src/util/bot/yasm-win32.exe.sha1 @@ -0,0 +1 @@ +4c4d1951181a610923523cb10d83d9ae9952fbf3
\ No newline at end of file diff --git a/src/util/clang-toolchain.cmake b/src/util/clang-toolchain.cmake deleted file mode 100644 index 8d81379..0000000 --- a/src/util/clang-toolchain.cmake +++ /dev/null @@ -1,2 +0,0 @@ -set(CMAKE_CXX_COMPILER "/agl/chromium/src/third_party/llvm-build/Release+Asserts/bin/clang++") -set(CMAKE_C_COMPILER "/agl/chromium/src/third_party/llvm-build/Release+Asserts/bin/clang") diff --git a/src/util/doc.config b/src/util/doc.config index 62db0f3..a427e04 100644 --- a/src/util/doc.config +++ b/src/util/doc.config @@ -9,10 +9,12 @@ "include/openssl/bytestring.h", "include/openssl/err.h", "include/openssl/cpu.h", + "include/openssl/crypto.h", "include/openssl/ex_data.h", "include/openssl/lhash.h", "include/openssl/mem.h", "include/openssl/obj.h", + "include/openssl/rand.h", "include/openssl/stack.h", "include/openssl/thread.h", "include/openssl/time_support.h" @@ -22,6 +24,7 @@ "Headers": [ "include/openssl/aes.h", "include/openssl/bn.h", + "include/openssl/cmac.h", "include/openssl/des.h", "include/openssl/dh.h", "include/openssl/dsa.h", @@ -45,5 +48,10 @@ "include/openssl/aead.h", "include/openssl/evp.h" ] + },{ + "Name": "SSL implementation", + "Headers": [ + "include/openssl/ssl.h" + ] }] } diff --git a/src/util/doc.go b/src/util/doc.go index 7c96af8..20feae5 100644 --- a/src/util/doc.go +++ b/src/util/doc.go @@ -111,7 +111,9 @@ func extractComment(lines []string, lineNo int) (comment []string, rest []string err = fmt.Errorf("comment doesn't start with block prefix on line %d: %s", restLineNo, line) return } - line = line[2:] + if len(line) == 2 || line[2] != '/' { + line = line[2:] + } if strings.HasPrefix(line, " ") { /* Identing the lines of a paragraph marks them as * preformatted. */ @@ -193,12 +195,23 @@ func extractDecl(lines []string, lineNo int) (decl string, rest []string, restLi func skipPast(s, skip string) string { i := strings.Index(s, skip) if i > 0 { - return s[len(skip):] + return s[i:] } return s } +func skipLine(s string) string { + i := strings.Index(s, "\n") + if i > 0 { + return s[i:] + } + return "" +} + func getNameFromDecl(decl string) (string, bool) { + for strings.HasPrefix(decl, "#if") { + decl = skipLine(decl) + } if strings.HasPrefix(decl, "struct ") { return "", false } @@ -582,8 +595,8 @@ func generateIndex(outPath string, config *Config, headerDescriptions map[string func main() { var ( - configFlag *string = flag.String("config", "", "Location of config file") - outputDir *string = flag.String("out", "", "Path to the directory where the output will be written") + configFlag *string = flag.String("config", "doc.config", "Location of config file") + outputDir *string = flag.String("out", ".", "Path to the directory where the output will be written") config Config ) diff --git a/src/util/generate_build_files.py b/src/util/generate_build_files.py new file mode 100644 index 0000000..94de546 --- /dev/null +++ b/src/util/generate_build_files.py @@ -0,0 +1,341 @@ +# Copyright (c) 2015, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +"""Enumerates the BoringSSL source in src/ and either generates two gypi files + (boringssl.gypi and boringssl_tests.gypi) for Chromium, or generates + source-list files for Android.""" + +import os +import subprocess +import sys + + +# OS_ARCH_COMBOS maps from OS and platform to the OpenSSL assembly "style" for +# that platform and the extension used by asm files. +OS_ARCH_COMBOS = [ + ('linux', 'arm', 'linux32', [], 'S'), + ('linux', 'aarch64', 'linux64', [], 'S'), + ('linux', 'x86', 'elf', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'), + ('linux', 'x86_64', 'elf', [], 'S'), + ('mac', 'x86', 'macosx', ['-fPIC', '-DOPENSSL_IA32_SSE2'], 'S'), + ('mac', 'x86_64', 'macosx', [], 'S'), + ('win', 'x86', 'win32n', ['-DOPENSSL_IA32_SSE2'], 'asm'), + ('win', 'x86_64', 'nasm', [], 'asm'), +] + +# NON_PERL_FILES enumerates assembly files that are not processed by the +# perlasm system. +NON_PERL_FILES = { + ('linux', 'arm'): [ + 'src/crypto/poly1305/poly1305_arm_asm.S', + 'src/crypto/chacha/chacha_vec_arm.S', + 'src/crypto/cpu-arm-asm.S', + ], +} + + +class Chromium(object): + + def __init__(self): + self.header = \ +"""# Copyright (c) 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This file is created by generate_build_files.py. Do not edit manually. + +""" + + def PrintVariableSection(self, out, name, files): + out.write(' \'%s\': [\n' % name) + for f in sorted(files): + out.write(' \'%s\',\n' % f) + out.write(' ],\n') + + def WriteFiles(self, files, asm_outputs): + with open('boringssl.gypi', 'w+') as gypi: + gypi.write(self.header + '{\n \'variables\': {\n') + + self.PrintVariableSection( + gypi, 'boringssl_lib_sources', files['crypto'] + files['ssl']) + + for ((osname, arch), asm_files) in asm_outputs: + self.PrintVariableSection(gypi, 'boringssl_%s_%s_sources' % + (osname, arch), asm_files) + + gypi.write(' }\n}\n') + + with open('boringssl_tests.gypi', 'w+') as test_gypi: + test_gypi.write(self.header + '{\n \'targets\': [\n') + + test_names = [] + for test in sorted(files['test']): + test_name = 'boringssl_%s' % os.path.splitext(os.path.basename(test))[0] + test_gypi.write(""" { + 'target_name': '%s', + 'type': 'executable', + 'dependencies': [ + 'boringssl.gyp:boringssl', + ], + 'sources': [ + '%s', + ], + # TODO(davidben): Fix size_t truncations in BoringSSL. + # https://crbug.com/429039 + 'msvs_disabled_warnings': [ 4267, ], + },\n""" % (test_name, test)) + test_names.append(test_name) + + test_names.sort() + + test_gypi.write(""" ], + 'variables': { + 'boringssl_test_targets': [\n""") + + for test in test_names: + test_gypi.write(""" '%s',\n""" % test) + + test_gypi.write(' ],\n }\n}\n') + + +class Android(object): + + def __init__(self): + self.header = \ +"""# Copyright (C) 2015 The Android Open Source Project +# +# Licensed 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. + +""" + + def PrintVariableSection(self, out, name, files): + out.write('%s := \\\n' % name) + for f in sorted(files): + out.write(' %s\\\n' % f) + out.write('\n') + + def WriteFiles(self, files, asm_outputs): + with open('sources.mk', 'w+') as makefile: + makefile.write(self.header) + + files['crypto'].append('android_compat_hacks.c') + files['crypto'].append('android_compat_keywrap.c') + self.PrintVariableSection(makefile, 'crypto_sources', files['crypto']) + self.PrintVariableSection(makefile, 'ssl_sources', files['ssl']) + self.PrintVariableSection(makefile, 'tool_sources', files['tool']) + + for ((osname, arch), asm_files) in asm_outputs: + self.PrintVariableSection( + makefile, '%s_%s_sources' % (osname, arch), asm_files) + + +def FindCMakeFiles(directory): + """Returns list of all CMakeLists.txt files recursively in directory.""" + cmakefiles = [] + + for (path, _, filenames) in os.walk(directory): + for filename in filenames: + if filename == 'CMakeLists.txt': + cmakefiles.append(os.path.join(path, filename)) + + return cmakefiles + + +def NoTests(dent, is_dir): + """Filter function that can be passed to FindCFiles in order to remove test + sources.""" + if is_dir: + return dent != 'test' + return 'test.' not in dent and not dent.startswith('example_') + + +def OnlyTests(dent, is_dir): + """Filter function that can be passed to FindCFiles in order to remove + non-test sources.""" + if is_dir: + return True + return '_test.' in dent or dent.startswith('example_') + + +def FindCFiles(directory, filter_func): + """Recurses through directory and returns a list of paths to all the C source + files that pass filter_func.""" + cfiles = [] + + for (path, dirnames, filenames) in os.walk(directory): + for filename in filenames: + if not filename.endswith('.c') and not filename.endswith('.cc'): + continue + if not filter_func(filename, False): + continue + cfiles.append(os.path.join(path, filename)) + + for (i, dirname) in enumerate(dirnames): + if not filter_func(dirname, True): + del dirnames[i] + + return cfiles + + +def ExtractPerlAsmFromCMakeFile(cmakefile): + """Parses the contents of the CMakeLists.txt file passed as an argument and + returns a list of all the perlasm() directives found in the file.""" + perlasms = [] + with open(cmakefile) as f: + for line in f: + line = line.strip() + if not line.startswith('perlasm('): + continue + if not line.endswith(')'): + raise ValueError('Bad perlasm line in %s' % cmakefile) + # Remove "perlasm(" from start and ")" from end + params = line[8:-1].split() + if len(params) < 2: + raise ValueError('Bad perlasm line in %s' % cmakefile) + perlasms.append({ + 'extra_args': params[2:], + 'input': os.path.join(os.path.dirname(cmakefile), params[1]), + 'output': os.path.join(os.path.dirname(cmakefile), params[0]), + }) + + return perlasms + + +def ReadPerlAsmOperations(): + """Returns a list of all perlasm() directives found in CMake config files in + src/.""" + perlasms = [] + cmakefiles = FindCMakeFiles('src') + + for cmakefile in cmakefiles: + perlasms.extend(ExtractPerlAsmFromCMakeFile(cmakefile)) + + return perlasms + + +def PerlAsm(output_filename, input_filename, perlasm_style, extra_args): + """Runs the a perlasm script and puts the output into output_filename.""" + base_dir = os.path.dirname(output_filename) + if not os.path.isdir(base_dir): + os.makedirs(base_dir) + output = subprocess.check_output( + ['perl', input_filename, perlasm_style] + extra_args) + with open(output_filename, 'w+') as out_file: + out_file.write(output) + + +def ArchForAsmFilename(filename): + """Returns the architectures that a given asm file should be compiled for + based on substrings in the filename.""" + + if 'x86_64' in filename or 'avx2' in filename: + return ['x86_64'] + elif ('x86' in filename and 'x86_64' not in filename) or '586' in filename: + return ['x86'] + elif 'armx' in filename: + return ['arm', 'aarch64'] + elif 'armv8' in filename: + return ['aarch64'] + elif 'arm' in filename: + return ['arm'] + else: + raise ValueError('Unknown arch for asm filename: ' + filename) + + +def WriteAsmFiles(perlasms): + """Generates asm files from perlasm directives for each supported OS x + platform combination.""" + asmfiles = {} + + for osarch in OS_ARCH_COMBOS: + (osname, arch, perlasm_style, extra_args, asm_ext) = osarch + key = (osname, arch) + outDir = '%s-%s' % key + + for perlasm in perlasms: + filename = os.path.basename(perlasm['input']) + output = perlasm['output'] + if not output.startswith('src'): + raise ValueError('output missing src: %s' % output) + output = os.path.join(outDir, output[4:]) + output = output.replace('${ASM_EXT}', asm_ext) + + if arch in ArchForAsmFilename(filename): + PerlAsm(output, perlasm['input'], perlasm_style, + perlasm['extra_args'] + extra_args) + asmfiles.setdefault(key, []).append(output) + + for (key, non_perl_asm_files) in NON_PERL_FILES.iteritems(): + asmfiles.setdefault(key, []).extend(non_perl_asm_files) + + return asmfiles + + +def main(platform): + crypto_c_files = FindCFiles(os.path.join('src', 'crypto'), NoTests) + ssl_c_files = FindCFiles(os.path.join('src', 'ssl'), NoTests) + tool_cc_files = FindCFiles(os.path.join('src', 'tool'), NoTests) + + # Generate err_data.c + with open('err_data.c', 'w+') as err_data: + subprocess.check_call(['go', 'run', 'err_data_generate.go'], + cwd=os.path.join('src', 'crypto', 'err'), + stdout=err_data) + crypto_c_files.append('err_data.c') + + test_c_files = FindCFiles(os.path.join('src', 'crypto'), OnlyTests) + test_c_files += FindCFiles(os.path.join('src', 'ssl'), OnlyTests) + + files = { + 'crypto': crypto_c_files, + 'ssl': ssl_c_files, + 'tool': tool_cc_files, + 'test': test_c_files, + } + + asm_outputs = sorted(WriteAsmFiles(ReadPerlAsmOperations()).iteritems()) + + platform.WriteFiles(files, asm_outputs) + + return 0 + + +def Usage(): + print 'Usage: python %s [chromium|android]' % sys.argv[0] + sys.exit(1) + + +if __name__ == '__main__': + if len(sys.argv) != 2: + Usage() + + platform = None + if sys.argv[1] == 'chromium': + platform = Chromium() + elif sys.argv[1] == 'android': + platform = Android() + else: + Usage() + + sys.exit(main(platform)) diff --git a/src/util/make_errors.go b/src/util/make_errors.go index 5fd75e2..dc8039a 100644 --- a/src/util/make_errors.go +++ b/src/util/make_errors.go @@ -36,17 +36,20 @@ const reservedReasonCode = 1000 var resetFlag *bool = flag.Bool("reset", false, "If true, ignore current assignments and reassign from scratch") func makeErrors(reset bool) error { - dirName, err := os.Getwd() + topLevelPath, err := findToplevel() if err != nil { return err } - lib := filepath.Base(dirName) - headerPath, err := findHeader(lib + ".h") + dirName, err := os.Getwd() if err != nil { return err } - sourcePath := lib + "_error.c" + + lib := filepath.Base(dirName) + headerPath := filepath.Join(topLevelPath, "include", "openssl", lib+".h") + errDir := filepath.Join(topLevelPath, "crypto", "err") + dataPath := filepath.Join(errDir, lib+".errordata") headerFile, err := os.Open(headerPath) if err != nil { @@ -90,7 +93,7 @@ func makeErrors(reset bool) error { } for _, name := range filenames { - if !strings.HasSuffix(name, ".c") || name == sourcePath { + if !strings.HasSuffix(name, ".c") { continue } @@ -119,55 +122,32 @@ func makeErrors(reset bool) error { } os.Rename(headerPath+".tmp", headerPath) - sourceFile, err := os.OpenFile(sourcePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) + dataFile, err := os.OpenFile(dataPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { return err } - defer sourceFile.Close() - fmt.Fprintf(sourceFile, `/* Copyright (c) 2014, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -#include <openssl/err.h> - -#include <openssl/%s.h> - -const ERR_STRING_DATA %s_error_string_data[] = { -`, lib, prefix) - outputStrings(sourceFile, lib, typeFunctions, functions) - outputStrings(sourceFile, lib, typeReasons, reasons) - - sourceFile.WriteString(" {0, NULL},\n};\n") + outputStrings(dataFile, lib, typeFunctions, functions) + outputStrings(dataFile, lib, typeReasons, reasons) + dataFile.Close() return nil } -func findHeader(basename string) (path string, err error) { - includeDir := filepath.Join("..", "include") +func findToplevel() (path string, err error) { + path = ".." + buildingPath := filepath.Join(path, "BUILDING") - fi, err := os.Stat(includeDir) + _, err = os.Stat(buildingPath) if err != nil && os.IsNotExist(err) { - includeDir = filepath.Join("..", includeDir) - fi, err = os.Stat(includeDir) + path = filepath.Join("..", path) + buildingPath = filepath.Join(path, "BUILDING") + _, err = os.Stat(buildingPath) } if err != nil { - return "", errors.New("cannot find path to include directory") - } - if !fi.IsDir() { - return "", errors.New("include node is not a directory") + return "", errors.New("Cannot find BUILDING file at the top-level") } - return filepath.Join(includeDir, "openssl", basename), nil + return path, nil } type assignment struct { @@ -295,18 +275,17 @@ func outputStrings(w io.Writer, lib string, ty int, assignments map[string]int) sort.Strings(keys) for _, key := range keys { - var pack string - if ty == typeFunctions { - pack = key + ", 0" - } else { - pack = "0, " + key + typeString := "function" + if ty == typeReasons { + typeString = "reason" } - - fmt.Fprintf(w, " {ERR_PACK(ERR_LIB_%s, %s), \"%s\"},\n", lib, pack, key[prefixLen:]) + fmt.Fprintf(w, "%s,%s,%d,%s\n", lib, typeString, assignments[key], key[prefixLen:]) } } func assignNewValues(assignments map[string]int, reserved int) { + // Needs to be in sync with the reason limit in + // |ERR_reason_error_string|. max := 99 for _, value := range assignments { @@ -320,16 +299,23 @@ func assignNewValues(assignments map[string]int, reserved int) { max++ + // Sort the keys, so this script is reproducible. + keys := make([]string, 0, len(assignments)) for key, value := range assignments { if value == -1 { - if reserved >= 0 && max >= reserved { - // If this happens, try passing - // -reset. Otherwise bump up reservedReasonCode. - panic("Automatically-assigned values exceeded limit!") - } - assignments[key] = max - max++ + keys = append(keys, key) + } + } + sort.Strings(keys) + + for _, key := range keys { + if reserved >= 0 && max >= reserved { + // If this happens, try passing -reset. Otherwise bump + // up reservedReasonCode. + panic("Automatically-assigned values exceeded limit!") } + assignments[key] = max + max++ } } @@ -360,8 +346,7 @@ func addFunctionsAndReasons(functions, reasons map[string]int, filename, prefix } defer file.Close() - prefix += "_" - reasonPrefix := prefix + "R_" + reasonPrefix := prefix + "_R_" var currentFunction string scanner := bufio.NewScanner(file) @@ -388,8 +373,9 @@ func addFunctionsAndReasons(functions, reasons map[string]int, filename, prefix } } - if strings.Contains(line, "OPENSSL_PUT_ERROR(") { - functionToken := prefix + "F_" + currentFunction + // Do not include cross-module error lines. + if strings.Contains(line, "OPENSSL_PUT_ERROR(" + prefix + ",") { + functionToken := prefix + "_F_" + currentFunction if _, ok := functions[functionToken]; !ok { functions[functionToken] = -1 } @@ -399,7 +385,7 @@ func addFunctionsAndReasons(functions, reasons map[string]int, filename, prefix handleDeclareMacro(line, "_F_", "OPENSSL_DECLARE_ERROR_FUNCTION(", functions) for len(line) > 0 { - i := strings.Index(line, prefix) + i := strings.Index(line, prefix + "_") if i == -1 { break } |