Skip to content

Commit f4ce18b

Browse files
author
Tor Didriksen
committed
Bug #25436469: BUILDS ARE NOT REPRODUCIBLE
Backport to 5.5 Current MySQL builds, even on Pushbuild, are not reproducible; they return different results depending on which directory they are built from (and Pushbuild uses several different directories). This is because absolute paths leak into debug information, and even worse, __FILE__. The latter moves code around enough that we've actually seen sysbench changes on the order of 4% in some tests. CMake seemingly insists on using absolute paths, but we can insert our own layer between CMake and GCC to relativize all paths. Also give the right flags to get debug information reproducible and turn off build stamping. This makes the mysqld build 100% bit-for-bit reproducible between runs on my machine, even when run from different directories.
1 parent af67680 commit f4ce18b

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

CMakeLists.txt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,36 @@ IF (ENABLE_GCOV AND NOT WIN32 AND NOT APPLE)
263263
SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fprofile-arcs -ftest-coverage -lgcov")
264264
ENDIF()
265265

266+
IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
267+
OPTION(REPRODUCIBLE_BUILD "Take extra pains to make build result independent of build location and time" OFF)
268+
ENDIF()
269+
IF(REPRODUCIBLE_BUILD)
270+
SET(DEBUG_PREFIX_FLAGS
271+
"-fdebug-prefix-map=${CMAKE_SOURCE_DIR}/=./ -fdebug-prefix-map=${CMAKE_CURRENT_BINARY_DIR}=./obj")
272+
273+
# See if -fdebug-prefix= commands are included in the debug output,
274+
# making the build unreproducible with switches recorded.
275+
# See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69821.
276+
EXECUTE_PROCESS(COMMAND ${CMAKE_C_COMPILER} -g3 -x c -S -fdebug-prefix-map=foo=bar -o - -
277+
INPUT_FILE /dev/null OUTPUT_VARIABLE DEBUG_PREFIX_MAP_RESULT)
278+
IF(DEBUG_PREFIX_MAP_RESULT MATCHES "foo=bar")
279+
SET(DEBUG_PREFIX_FLAGS "${DEBUG_PREFIX_FLAGS} -gno-record-gcc-switches")
280+
ENDIF()
281+
282+
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${DEBUG_PREFIX_FLAGS}")
283+
SET(CMAKE_C_FLAGS_RELWITHDEBINFO
284+
"${CMAKE_C_FLAGS_RELWITHDEBINFO} ${DEBUG_PREFIX_FLAGS}")
285+
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEBUG_PREFIX_FLAGS}")
286+
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO
287+
"${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${DEBUG_PREFIX_FLAGS}")
288+
289+
SET(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--build-id=none")
290+
SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--build-id=none")
291+
292+
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE
293+
"${CMAKE_SOURCE_DIR}/scripts/invoke-with-relative-paths.pl")
294+
ENDIF()
295+
266296
OPTION(ENABLED_LOCAL_INFILE
267297
"If we should should enable LOAD DATA LOCAL by default" ${IF_WIN})
268298
MARK_AS_ADVANCED(ENABLED_LOCAL_INFILE)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#! /usr/bin/perl
2+
#
3+
# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
4+
#
5+
# This program is free software; you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation; version 2 of the License.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program; if not, write to the Free Software Foundation,
16+
# 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17+
#
18+
19+
#
20+
# Take the given GCC command line and run it with all absolute paths
21+
# changed to relative paths. This makes sure that no part of the build
22+
# path leaks into the .o files, which it normally would through the
23+
# contents of __FILE__. (Debug information is also affected, but that
24+
# is already fixed through -fdebug-prefix-map=.)
25+
#
26+
# A more elegant solution would be -ffile-prefix-map=, but this is
27+
# not currently supported in GCC; see
28+
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70268.
29+
#
30+
31+
use strict;
32+
use warnings;
33+
use Cwd;
34+
35+
my $cwd = getcwd();
36+
37+
my @newarg = ();
38+
for my $i (0..$#ARGV) {
39+
my $arg = $ARGV[$i];
40+
if ($arg =~ /-I(.+)$/) {
41+
$arg = '-I' . relativize($1, $cwd);
42+
} elsif ($arg =~ /^\//) {
43+
$arg = relativize($arg, $cwd);
44+
}
45+
push @newarg, $arg;
46+
}
47+
48+
exec(@newarg);
49+
50+
# /a/b/c/foo from /a/b/d = ../c/foo
51+
sub relativize {
52+
my ($dir1, $dir2) = @_;
53+
54+
if ($dir1 !~ /^\//) {
55+
# Not an absolute path.
56+
return $dir1;
57+
}
58+
59+
if (! -e $dir1) {
60+
# print STDERR "Unknown file/directory $dir1.\n";
61+
return $dir1;
62+
}
63+
# Resolve symlinks and such, because getcwd() does.
64+
$dir1 = Cwd::abs_path($dir1);
65+
66+
if ($dir1 =~ /^\/(lib|tmp|usr)/) {
67+
# Not related to our source code.
68+
return $dir1;
69+
}
70+
71+
if ($dir1 eq $dir2) {
72+
return ".";
73+
}
74+
75+
my (@dir1_components) = split /\//, $dir1;
76+
my (@dir2_components) = split /\//, $dir2;
77+
78+
# Remove common leading components.
79+
while (scalar @dir1_components > 0 && scalar @dir2_components > 0 &&
80+
$dir1_components[0] eq $dir2_components[0]) {
81+
shift @dir1_components;
82+
shift @dir2_components;
83+
}
84+
85+
my $ret = "";
86+
for my $i (0..$#dir2_components) {
87+
$ret .= '../';
88+
}
89+
$ret .= join('/', @dir1_components);
90+
91+
# print STDERR "[$dir1] from [$dir2] => [$ret]\n";
92+
93+
return $ret;
94+
}
95+

0 commit comments

Comments
 (0)