| 
									
										
										
										
											2024-06-02 15:14:25 +01:00
										 |  |  | # typed: strict | 
					
						
							| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 10:39:15 -05:00
										 |  |  | require "cache_store" | 
					
						
							| 
									
										
										
										
											2018-02-28 09:13:17 -08:00
										 |  |  | require "linkage_checker" | 
					
						
							| 
									
										
										
										
											2016-07-14 13:14:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  | module FormulaCellarChecks | 
					
						
							| 
									
										
										
										
											2024-03-24 19:46:57 -04:00
										 |  |  |   sig { returns(T.nilable(String)) } | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |   def check_shadowed_headers | 
					
						
							|  |  |  |     return if ["libtool", "subversion", "berkeley-db"].any? do |formula_name| | 
					
						
							|  |  |  |       formula.name.start_with?(formula_name) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 14:01:53 -07:00
										 |  |  |     return if formula.name.match?(Version.formula_optionally_versioned_regex(:php)) | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |     return if formula.keg_only? || !formula.include.directory? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     files  = relative_glob(formula.include, "**/*.h") | 
					
						
							|  |  |  |     files &= relative_glob("#{MacOS.sdk_path}/usr/include", "**/*.h") | 
					
						
							|  |  |  |     files.map! { |p| File.join(formula.include, p) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return if files.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-15 02:28:32 +02:00
										 |  |  |     <<~EOS | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |       Header files that shadow system header files were installed to "#{formula.include}" | 
					
						
							|  |  |  |       The offending files are: | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:23 +08:00
										 |  |  |         #{files * "\n  "} | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |     EOS | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-24 19:46:57 -04:00
										 |  |  |   sig { returns(T.nilable(String)) } | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |   def check_openssl_links | 
					
						
							|  |  |  |     return unless formula.prefix.directory? | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |     keg = Keg.new(formula.prefix) | 
					
						
							|  |  |  |     system_openssl = keg.mach_o_files.select do |obj| | 
					
						
							|  |  |  |       dlls = obj.dynamically_linked_libraries | 
					
						
							| 
									
										
										
										
											2023-12-27 13:00:48 -08:00
										 |  |  |       dlls.any? { |dll| %r{/usr/lib/lib(crypto|ssl|tls)\..*dylib}.match? dll } | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |     end | 
					
						
							|  |  |  |     return if system_openssl.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-15 02:28:32 +02:00
										 |  |  |     <<~EOS | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |       object files were linked against system openssl | 
					
						
							| 
									
										
										
										
											2016-09-04 16:28:51 -07:00
										 |  |  |       These object files were linked against the deprecated system OpenSSL or | 
					
						
							|  |  |  |       the system's private LibreSSL. | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |       Adding `depends_on "openssl"` to the formula may help. | 
					
						
							| 
									
										
										
										
											2021-10-13 16:54:02 +08:00
										 |  |  |         #{system_openssl * "\n  "} | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |     EOS | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-24 19:46:57 -04:00
										 |  |  |   sig { params(lib: Pathname).returns(T.nilable(String)) } | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |   def check_python_framework_links(lib) | 
					
						
							|  |  |  |     python_modules = Pathname.glob lib/"python*/site-packages/**/*.so" | 
					
						
							|  |  |  |     framework_links = python_modules.select do |obj| | 
					
						
							|  |  |  |       dlls = obj.dynamically_linked_libraries | 
					
						
							| 
									
										
										
										
											2023-02-06 13:48:18 -05:00
										 |  |  |       dlls.any? { |dll| dll.include?("Python.framework") } | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |     end | 
					
						
							|  |  |  |     return if framework_links.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-15 02:28:32 +02:00
										 |  |  |     <<~EOS | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |       python modules have explicit framework links | 
					
						
							|  |  |  |       These python extension modules were linked directly to a Python | 
					
						
							|  |  |  |       framework binary. They should be linked with -undefined dynamic_lookup | 
					
						
							|  |  |  |       instead of -lpython or -framework Python. | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:23 +08:00
										 |  |  |         #{framework_links * "\n  "} | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |     EOS | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-25 15:55:37 -04:00
										 |  |  |   sig { void } | 
					
						
							| 
									
										
										
										
											2016-07-14 13:14:03 +08:00
										 |  |  |   def check_linkage | 
					
						
							|  |  |  |     return unless formula.prefix.directory? | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-14 13:14:03 +08:00
										 |  |  |     keg = Keg.new(formula.prefix) | 
					
						
							| 
									
										
										
										
											2018-02-24 17:32:29 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-22 14:46:14 +01:00
										 |  |  |     CacheStoreDatabase.use(:linkage) do |db| | 
					
						
							| 
									
										
										
										
											2018-06-01 13:27:17 +01:00
										 |  |  |       checker = LinkageChecker.new(keg, formula, cache_db: db) | 
					
						
							| 
									
										
										
										
											2018-04-25 10:27:03 -04:00
										 |  |  |       next unless checker.broken_library_linkage? | 
					
						
							| 
									
										
										
										
											2018-05-22 14:46:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-09 14:19:07 -04:00
										 |  |  |       output = <<~EOS | 
					
						
							|  |  |  |         #{formula} has broken dynamic library links: | 
					
						
							| 
									
										
										
										
											2018-04-24 16:49:51 -04:00
										 |  |  |           #{checker.display_test_output} | 
					
						
							| 
									
										
										
										
											2017-08-07 11:21:55 +01:00
										 |  |  |       EOS | 
					
						
							| 
									
										
										
										
											2018-04-09 14:19:07 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-28 03:23:21 +02:00
										 |  |  |       tab = keg.tab | 
					
						
							| 
									
										
										
										
											2018-04-09 14:19:07 -04:00
										 |  |  |       if tab.poured_from_bottle | 
					
						
							|  |  |  |         output += <<~EOS | 
					
						
							|  |  |  |           Rebuild this from source with: | 
					
						
							|  |  |  |             brew reinstall --build-from-source #{formula} | 
					
						
							| 
									
										
										
										
											2023-07-24 14:01:53 -07:00
										 |  |  |           If that's successful, file an issue#{formula.tap ? " here:\n  #{T.must(formula.tap).issues_url}" : "."} | 
					
						
							| 
									
										
										
										
											2018-04-09 14:19:07 -04:00
										 |  |  |         EOS | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |       problem_if_output output | 
					
						
							| 
									
										
										
										
											2017-08-07 11:21:55 +01:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2016-07-14 13:14:03 +08:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-24 19:46:57 -04:00
										 |  |  |   sig { params(formula: Formula).returns(T.nilable(String)) } | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:23 +08:00
										 |  |  |   def check_flat_namespace(formula) | 
					
						
							|  |  |  |     return unless formula.prefix.directory? | 
					
						
							| 
									
										
										
										
											2024-03-24 19:46:57 -04:00
										 |  |  |     return if formula.tap&.audit_exception(:flat_namespace_allowlist, formula.name) | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-21 12:24:21 -07:00
										 |  |  |     keg = ::Keg.new(formula.prefix) | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:23 +08:00
										 |  |  |     flat_namespace_files = keg.mach_o_files.reject do |file| | 
					
						
							|  |  |  |       next true unless file.dylib? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       macho = MachO.open(file) | 
					
						
							| 
									
										
										
										
											2021-10-13 16:54:02 +08:00
										 |  |  |       if MachO::Utils.fat_magic?(macho.magic) | 
					
						
							| 
									
										
										
										
											2021-10-12 15:35:46 +08:00
										 |  |  |         macho.machos.map(&:header).all? { |h| h.flag? :MH_TWOLEVEL } | 
					
						
							| 
									
										
										
										
											2021-10-12 13:49:53 +08:00
										 |  |  |       else | 
					
						
							| 
									
										
										
										
											2021-10-13 16:54:02 +08:00
										 |  |  |         macho.header.flag? :MH_TWOLEVEL | 
					
						
							| 
									
										
										
										
											2021-10-12 13:49:53 +08:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:23 +08:00
										 |  |  |     end | 
					
						
							|  |  |  |     return if flat_namespace_files.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     <<~EOS | 
					
						
							|  |  |  |       Libraries were compiled with a flat namespace. | 
					
						
							| 
									
										
										
										
											2024-04-30 11:10:23 +02:00
										 |  |  |       This can cause linker errors due to name collisions and | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:23 +08:00
										 |  |  |       is often due to a bug in detecting the macOS version. | 
					
						
							|  |  |  |         #{flat_namespace_files * "\n  "} | 
					
						
							|  |  |  |     EOS | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-24 19:46:57 -04:00
										 |  |  |   sig { void } | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |   def audit_installed | 
					
						
							|  |  |  |     generic_audit_installed | 
					
						
							| 
									
										
										
										
											2017-04-18 08:17:24 +01:00
										 |  |  |     problem_if_output(check_shadowed_headers) | 
					
						
							|  |  |  |     problem_if_output(check_openssl_links) | 
					
						
							|  |  |  |     problem_if_output(check_python_framework_links(formula.lib)) | 
					
						
							| 
									
										
										
										
											2016-07-14 13:14:03 +08:00
										 |  |  |     check_linkage | 
					
						
							| 
									
										
										
										
											2021-10-12 13:11:23 +08:00
										 |  |  |     problem_if_output(check_flat_namespace(formula)) | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  |   end | 
					
						
							| 
									
										
										
										
											2018-08-22 21:25:00 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-24 19:46:57 -04:00
										 |  |  |   sig { params(filename: Pathname).returns(T::Boolean) } | 
					
						
							| 
									
										
										
										
											2018-08-22 21:25:00 -05:00
										 |  |  |   def valid_library_extension?(filename) | 
					
						
							|  |  |  |     macos_lib_extensions = %w[.dylib .framework] | 
					
						
							|  |  |  |     generic_valid_library_extension?(filename) || macos_lib_extensions.include?(filename.extname) | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-07-09 13:51:53 +01:00
										 |  |  | end |