|
| 1 | +# rubocop:disable Style/StderrPuts |
| 2 | +# rubocop:disable Style/GlobalVars |
| 3 | + |
| 4 | +require 'rubygems' |
| 5 | +require_relative '../libdatadog_extconf_helpers' |
| 6 | + |
| 7 | +def skip_building_extension!(reason) |
| 8 | + $stderr.puts( |
| 9 | + "WARN: Skipping build of datadog_runtime_stacks (#{reason}). Runtime stack capture will not be available." |
| 10 | + ) |
| 11 | + |
| 12 | + fail_install_if_missing_extension = ENV['DD_FAIL_INSTALL_IF_MISSING_EXTENSION'].to_s.strip.downcase == 'true' |
| 13 | + |
| 14 | + if fail_install_if_missing_extension |
| 15 | + require 'mkmf' |
| 16 | + Logging.message("[datadog] Failure cause: #{reason}") |
| 17 | + else |
| 18 | + File.write('Makefile', 'all install clean: # dummy makefile that does nothing') |
| 19 | + end |
| 20 | + |
| 21 | + exit |
| 22 | +end |
| 23 | + |
| 24 | +if ENV['DD_NO_EXTENSION'].to_s.strip.downcase == 'true' |
| 25 | + skip_building_extension!('the `DD_NO_EXTENSION` environment variable is/was set to `true` during installation') |
| 26 | +end |
| 27 | +skip_building_extension!('current Ruby VM is not supported') if RUBY_ENGINE != 'ruby' |
| 28 | +skip_building_extension!('Microsoft Windows is not supported') if Gem.win_platform? |
| 29 | + |
| 30 | +libdatadog_issue = Datadog::LibdatadogExtconfHelpers.load_libdatadog_or_get_issue |
| 31 | +skip_building_extension!("issue setting up `libdatadog` gem: #{libdatadog_issue}") if libdatadog_issue |
| 32 | + |
| 33 | +require 'mkmf' |
| 34 | + |
| 35 | +# Because we can't control what compiler versions our customers use, shipping with -Werror by default is a no-go. |
| 36 | +# But we can enable it in CI, so that we quickly spot any new warnings that just got introduced. |
| 37 | +append_cflags '-Werror' if ENV['DATADOG_GEM_CI'] == 'true' |
| 38 | + |
| 39 | +# Older gcc releases may not default to C99 and we need to ask for this. This is also used: |
| 40 | +# * by upstream Ruby -- search for gnu99 in the codebase |
| 41 | +# * by msgpack, another datadog gem dependency |
| 42 | +# (https://github.com/msgpack/msgpack-ruby/blob/18ce08f6d612fe973843c366ac9a0b74c4e50599/ext/msgpack/extconf.rb#L8) |
| 43 | +append_cflags '-std=gnu99' |
| 44 | + |
| 45 | +# Gets really noisy when we include the MJIT header, let's omit it (TODO: Use #pragma GCC diagnostic instead?) |
| 46 | +append_cflags "-Wno-unused-function" |
| 47 | + |
| 48 | +# Allow defining variables at any point in a function |
| 49 | +append_cflags '-Wno-declaration-after-statement' |
| 50 | + |
| 51 | +# If we forget to include a Ruby header, the function call may still appear to work, but then |
| 52 | +# cause a segfault later. Let's ensure that never happens. |
| 53 | +append_cflags '-Werror-implicit-function-declaration' |
| 54 | + |
| 55 | +# The native extension is not intended to expose any symbols/functions for other native libraries to use; |
| 56 | +# the sole exception being `Init_datadog_runtime_stacks` which needs to be visible for Ruby to call it when |
| 57 | +# it `dlopen`s the library. |
| 58 | +# |
| 59 | +# By setting this compiler flag, we tell it to assume that everything is private unless explicitly stated. |
| 60 | +# For more details see https://gcc.gnu.org/wiki/Visibility |
| 61 | +append_cflags '-fvisibility=hidden' |
| 62 | + |
| 63 | +# Avoid legacy C definitions |
| 64 | +append_cflags '-Wold-style-definition' |
| 65 | + |
| 66 | +# Enable all other compiler warnings |
| 67 | +append_cflags '-Wall' |
| 68 | +append_cflags '-Wextra' |
| 69 | + |
| 70 | +if ENV['DDTRACE_DEBUG'] == 'true' |
| 71 | + $defs << '-DDD_DEBUG' |
| 72 | + CONFIG['optflags'] = '-O0' |
| 73 | + CONFIG['debugflags'] = '-ggdb3' |
| 74 | +end |
| 75 | + |
| 76 | +# If we got here, libdatadog is available and loaded |
| 77 | +ENV['PKG_CONFIG_PATH'] = "#{ENV["PKG_CONFIG_PATH"]}:#{Libdatadog.pkgconfig_folder}" |
| 78 | +Logging.message("[datadog] PKG_CONFIG_PATH set to #{ENV["PKG_CONFIG_PATH"].inspect}\n") |
| 79 | +$stderr.puts("Using libdatadog #{Libdatadog::VERSION} from #{Libdatadog.pkgconfig_folder}") |
| 80 | + |
| 81 | +unless pkg_config('datadog_profiling_with_rpath') |
| 82 | + Logging.message("[datadog] Ruby detected the pkg-config command is #{$PKGCONFIG.inspect}\n") |
| 83 | + |
| 84 | + if Datadog::LibdatadogExtconfHelpers.pkg_config_missing? |
| 85 | + skip_building_extension!('the `pkg-config` system tool is missing') |
| 86 | + else |
| 87 | + skip_building_extension!('there was a problem in setting up the `libdatadog` dependency') |
| 88 | + end |
| 89 | +end |
| 90 | + |
| 91 | +# See comments on the helper methods being used for why we need to additionally set this. |
| 92 | +# The extremely excessive escaping around ORIGIN below seems to be correct and was determined after a lot of |
| 93 | +# experimentation. We need to get these special characters across a lot of tools untouched... |
| 94 | +extra_relative_rpaths = [ |
| 95 | + Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_native_lib_folder(current_folder: __dir__), |
| 96 | + *Datadog::LibdatadogExtconfHelpers.libdatadog_folder_relative_to_ruby_extensions_folders, |
| 97 | +] |
| 98 | +extra_relative_rpaths.each { |folder| $LDFLAGS += " -Wl,-rpath,$$$\\\\{ORIGIN\\}/#{folder.to_str}" } |
| 99 | +Logging.message("[datadog] After pkg-config $LDFLAGS were set to: #{$LDFLAGS.inspect}\n") |
| 100 | + |
| 101 | +# Enable access to Ruby VM internal headers for runtime stack walking |
| 102 | +# Ruby version compatibility definitions |
| 103 | + |
| 104 | +# On Ruby 3.5, we can't ask the object_id from IMEMOs (https://github.com/ruby/ruby/pull/13347) |
| 105 | +$defs << "-DNO_IMEMO_OBJECT_ID" unless RUBY_VERSION < "3.5" |
| 106 | + |
| 107 | +# On Ruby 2.5 and 3.3, this symbol was not visible. It is on 2.6 to 3.2, as well as 3.4+ |
| 108 | +$defs << "-DNO_RB_OBJ_INFO" if RUBY_VERSION.start_with?("2.5", "3.3") |
| 109 | + |
| 110 | +# On older Rubies, M:N threads were not available |
| 111 | +$defs << "-DNO_MN_THREADS_AVAILABLE" if RUBY_VERSION < "3.3" |
| 112 | + |
| 113 | +# On older Rubies, we did not need to include the ractor header (this was built into the MJIT header) |
| 114 | +$defs << "-DNO_RACTOR_HEADER_INCLUDE" if RUBY_VERSION < "3.3" |
| 115 | + |
| 116 | +# On older Rubies, some of the Ractor internal APIs were directly accessible |
| 117 | +$defs << "-DUSE_RACTOR_INTERNAL_APIS_DIRECTLY" if RUBY_VERSION < "3.3" |
| 118 | + |
| 119 | +# On older Rubies, the first_lineno inside a location was a VALUE and not a int (https://github.com/ruby/ruby/pull/6430) |
| 120 | +$defs << "-DNO_INT_FIRST_LINENO" if RUBY_VERSION < "3.2" |
| 121 | + |
| 122 | +# On older Rubies, there are no Ractors |
| 123 | +$defs << "-DNO_RACTORS" if RUBY_VERSION < "3" |
| 124 | + |
| 125 | +# On older Rubies, rb_imemo_name did not exist |
| 126 | +$defs << "-DNO_IMEMO_NAME" if RUBY_VERSION < "3" |
| 127 | + |
| 128 | +# Tag the native extension library with the Ruby version and Ruby platform. |
| 129 | +# This makes it easier for development (avoids "oops I forgot to rebuild when I switched my Ruby") and ensures that |
| 130 | +# the wrong library is never loaded. |
| 131 | +# When requiring, we need to use the exact same string, including the version and the platform. |
| 132 | +EXTENSION_NAME = "datadog_runtime_stacks.#{RUBY_VERSION[/\d+.\d+/]}_#{RUBY_PLATFORM}".freeze |
| 133 | + |
| 134 | +CAN_USE_MJIT_HEADER = RUBY_VERSION.start_with?("2.6", "2.7", "3.0.", "3.1.", "3.2.") |
| 135 | + |
| 136 | +mjit_header_worked = false |
| 137 | + |
| 138 | +if CAN_USE_MJIT_HEADER |
| 139 | + mjit_header_file_name = "rb_mjit_min_header-#{RUBY_VERSION}.h" |
| 140 | + |
| 141 | + # Validate that the mjit header can actually be compiled on this system. |
| 142 | + original_common_headers = MakeMakefile::COMMON_HEADERS |
| 143 | + MakeMakefile::COMMON_HEADERS = "".freeze |
| 144 | + if have_macro("RUBY_MJIT_H", mjit_header_file_name) |
| 145 | + MakeMakefile::COMMON_HEADERS = original_common_headers |
| 146 | + |
| 147 | + $defs << "-DRUBY_MJIT_HEADER='\"#{mjit_header_file_name}\"'" |
| 148 | + |
| 149 | + # NOTE: This needs to come after all changes to $defs |
| 150 | + create_header |
| 151 | + |
| 152 | + # Note: -Wunused-parameter flag is intentionally added here only after MJIT header validation |
| 153 | + append_cflags "-Wunused-parameter" |
| 154 | + |
| 155 | + create_makefile(EXTENSION_NAME) |
| 156 | + mjit_header_worked = true |
| 157 | + else |
| 158 | + # MJIT header compilation failed, fallback to datadog-ruby_core_source |
| 159 | + $stderr.puts "MJIT header compilation failed, falling back to datadog-ruby_core_source" |
| 160 | + MakeMakefile::COMMON_HEADERS = original_common_headers |
| 161 | + end |
| 162 | +end |
| 163 | + |
| 164 | +# The MJIT header was introduced on 2.6 and removed on 3.3; for other Rubies we rely on |
| 165 | +# the datadog-ruby_core_source gem to get access to private VM headers. |
| 166 | +# We also use it as a fallback when MJIT header compilation fails. |
| 167 | +unless mjit_header_worked |
| 168 | + create_header |
| 169 | + |
| 170 | + require "datadog/ruby_core_source" |
| 171 | + dir_config("ruby") # allow user to pass in non-standard core include directory |
| 172 | + |
| 173 | + # Workaround for mkmf issue with $CPPFLAGS |
| 174 | + Datadog::RubyCoreSource.define_singleton_method(:with_cppflags) do |newflags, &block| |
| 175 | + super("#{newflags} #{$CPPFLAGS}", &block) |
| 176 | + end |
| 177 | + |
| 178 | + makefile_created = Datadog::RubyCoreSource |
| 179 | + .create_makefile_with_core( |
| 180 | + proc do |
| 181 | + headers_available = |
| 182 | + have_header("vm_core.h") && |
| 183 | + have_header("iseq.h") && |
| 184 | + (RUBY_VERSION < "3.3" || have_header("ractor_core.h")) |
| 185 | + |
| 186 | + if headers_available |
| 187 | + append_cflags "-Wunused-parameter" |
| 188 | + end |
| 189 | + |
| 190 | + headers_available |
| 191 | + end, |
| 192 | + EXTENSION_NAME |
| 193 | + ) |
| 194 | + |
| 195 | + unless makefile_created |
| 196 | + skip_building_extension!('required Ruby VM internal headers are not available for this Ruby version') |
| 197 | + end |
| 198 | +end |
| 199 | + |
| 200 | +# rubocop:enable Style/GlobalVars |
| 201 | +# rubocop:enable Style/StderrPuts |
0 commit comments