diff --git a/cpp/src/arrow/meson.build b/cpp/src/arrow/meson.build
index 3f30b6b666350..8cedd7b9ab544 100644
--- a/cpp/src/arrow/meson.build
+++ b/cpp/src/arrow/meson.build
@@ -128,6 +128,7 @@ arrow_components = {
             'util/logger.cc',
             'util/logging.cc',
             'util/key_value_metadata.cc',
+            'util/math_internal.cc',
             'util/memory.cc',
             'util/mutex.cc',
             'util/ree_util.cc',
@@ -352,7 +353,7 @@ install_headers(
         'visit_scalar_inline.h',
         'visit_type_inline.h',
     ],
-    install_dir: 'arrow',
+    subdir: 'arrow',
 )
 
 if needs_testing
@@ -371,10 +372,12 @@ if needs_testing
         filesystem_dep = boost_proj.dependency('boost_filesystem')
     endif
 
+    gtest_dep = dependency('gtest')
     gtest_main_dep = dependency('gtest_main')
     gmock_dep = dependency('gmock')
 else
     filesystem_dep = disabler()
+    gtest_dep = disabler()
     gtest_main_dep = disabler()
     gmock_dep = disabler()
 endif
diff --git a/cpp/src/arrow/testing/meson.build b/cpp/src/arrow/testing/meson.build
index d1ebfe2eddbb0..1b6e6a9540d93 100644
--- a/cpp/src/arrow/testing/meson.build
+++ b/cpp/src/arrow/testing/meson.build
@@ -35,6 +35,7 @@ install_headers(
         'util.h',
         'visibility.h',
     ],
+    subdir: 'arrow/testing',
 )
 
 if needs_tests
diff --git a/cpp/src/arrow/util/meson.build b/cpp/src/arrow/util/meson.build
index 016ef06a0826f..df752247b607e 100644
--- a/cpp/src/arrow/util/meson.build
+++ b/cpp/src/arrow/util/meson.build
@@ -92,3 +92,242 @@ configure_file(
     configuration: internal_conf_data,
     format: 'cmake@',
 )
+
+install_headers(
+    [
+        'algorithm.h',
+        'aligned_storage.h',
+        'align_util.h',
+        'async_generator_fwd.h',
+        'async_generator.h',
+        'async_util.h',
+        'base64.h',
+        'basic_decimal.h',
+        'benchmark_util.h',
+        'binary_view_util.h',
+        'bit_block_counter.h',
+        'bitmap_builders.h',
+        'bitmap_generate.h',
+        'bitmap.h',
+        'bitmap_ops.h',
+        'bitmap_reader.h',
+        'bitmap_visit.h',
+        'bitmap_writer.h',
+        'bit_run_reader.h',
+        'bitset_stack.h',
+        'bit_util.h',
+        'bpacking64_default.h',
+        'bpacking_avx2.h',
+        'bpacking_avx512.h',
+        'bpacking_default.h',
+        'bpacking.h',
+        'bpacking_neon.h',
+        'byte_size.h',
+        'cancel.h',
+        'checked_cast.h',
+        'compare.h',
+        'compression.h',
+        'concurrent_map.h',
+        'converter.h',
+        'counting_semaphore.h',
+        'cpu_info.h',
+        'crc32.h',
+        'debug.h',
+        'decimal.h',
+        'delimiting.h',
+        'dict_util.h',
+        'dispatch.h',
+        'double_conversion.h',
+        'endian.h',
+        'float16.h',
+        'formatting.h',
+        'functional.h',
+        'future.h',
+        'hashing.h',
+        'hash_util.h',
+        'int_util.h',
+        'int_util_overflow.h',
+        'io_util.h',
+        'iterator.h',
+        'key_value_metadata.h',
+        'launder.h',
+        'list_util.h',
+        'logger.h',
+        'logging.h',
+        'macros.h',
+        'map.h',
+        'math_constants.h',
+        'memory.h',
+        'mutex.h',
+        'parallel.h',
+        'pcg_random.h',
+        'prefetch.h',
+        'print.h',
+        'queue.h',
+        'range.h',
+        'ree_util.h',
+        'regex.h',
+        'rows_to_batches.h',
+        'simd.h',
+        'small_vector.h',
+        'sort.h',
+        'spaced.h',
+        'span.h',
+        'stopwatch.h',
+        'string_builder.h',
+        'string.h',
+        'task_group.h',
+        'tdigest.h',
+        'test_common.h',
+        'thread_pool.h',
+        'time.h',
+        'tracing.h',
+        'trie.h',
+        'type_fwd.h',
+        'type_traits.h',
+        'ubsan.h',
+        'union_util.h',
+        'unreachable.h',
+        'uri.h',
+        'utf8.h',
+        'value_parsing.h',
+        'vector.h',
+        'visibility.h',
+        'windows_compatibility.h',
+        'windows_fixup.h',
+    ],
+    subdir: 'arrow/util',
+)
+
+utility_test_srcs = [
+    'align_util_test.cc',
+    'atfork_test.cc',
+    'byte_size_test.cc',
+    'byte_stream_split_test.cc',
+    'cache_test.cc',
+    'checked_cast_test.cc',
+    'compression_test.cc',
+    'decimal_test.cc',
+    'float16_test.cc',
+    'fixed_width_test.cc',
+    'formatting_util_test.cc',
+    'key_value_metadata_test.cc',
+    'hashing_test.cc',
+    'int_util_test.cc',
+    'io_util_test.cc',
+    'iterator_test.cc',
+    'list_util_test.cc',
+    'logger_test.cc',
+    'logging_test.cc',
+    'math_test.cc',
+    'queue_test.cc',
+    'range_test.cc',
+    'ree_util_test.cc',
+    'reflection_test.cc',
+    'rows_to_batches_test.cc',
+    'small_vector_test.cc',
+    'span_test.cc',
+    'stl_util_test.cc',
+    'string_test.cc',
+    'tdigest_test.cc',
+    'test_common.cc',
+    'time_test.cc',
+    'tracing_test.cc',
+    'trie_test.cc',
+    'uri_test.cc',
+    'utf8_util_test.cc',
+    'value_parsing_test.cc',
+]
+
+if host_machine.system() == 'windows'
+    # This manifest enables long file paths on Windows 10+
+    # See https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#enable-long-paths-in-windows-10-version-1607-and-later
+    if cpp_compiler.get_id() == 'msvc'
+        utility_test_sources += ['io_util_test.manifest']
+    else
+        utility_test_sources += ['io_util_test.rc']
+    endif
+endif
+
+exc = executable(
+    'arrow-utility-test',
+    sources: utility_test_srcs,
+    dependencies: [arrow_dep, filesystem_dep, gtest_dep, gmock_dep],
+    link_with: [arrow_test_lib],
+    implicit_include_directories: false,
+)
+test('arrow-utility-test', exc)
+
+util_tests = {
+    'arrow-async-utility-test': {
+        'sources': [
+            'async_generator_test.cc',
+            'async_util_test.cc',
+            'test_common.cc',
+        ],
+    },
+    'arrow-bit-utility-test': {
+        'sources': [
+            'bit_block_counter_test.cc',
+            'bit_util_test.cc',
+            'rle_encoding_test.cc',
+        ],
+    },
+    'arrow-threading-utility-test': {
+        'sources': [
+            'cancel_test.cc',
+            'counting_semaphore_test.cc',
+            'future_test.cc',
+            'task_group_test.cc',
+            'test_common.cc',
+            'thread_pool_test.cc',
+        ],
+    },
+    'arrow-crc32-test': {
+        'sources': ['crc32_test.cc'],
+        'dependencies': [filesystem_dep],
+    },
+}
+
+if needs_tests
+    foreach key, val : util_tests
+        exc = executable(
+            key,
+            sources: val['sources'],
+            dependencies: [arrow_test_dep, val.get('dependencies', [])],
+            implicit_include_directories: false,
+        )
+        test(key, exc)
+    endforeach
+endif
+
+util_benchmarks = [
+    'bit_block_counter',
+    'bit_util',
+    'bitmap_reader',
+    'cache',
+    'compression',
+    'decimal',
+    'hashing',
+    'int_util',
+    'machine',
+    'queue',
+    'range',
+    'small_vector',
+    'tdigest',
+    'thread_pool',
+    'trie',
+]
+
+foreach util_benchmark : util_benchmarks
+    benchmark_name = '@0@-benchmark'.format(util_benchmark.replace('_', '-'))
+    exc = executable(
+        benchmark_name,
+        sources: '@0@_benchmark.cc'.format(util_benchmark),
+        dependencies: [arrow_test_dep, benchmark_dep],
+        implicit_include_directories: false,
+    )
+    benchmark(benchmark_name, exc)
+endforeach
+
+# TODO: XSimd benchmark. See GH-45823