MakeMakefile
MakeMakefile (mkmf)
Ruby C 확장이 C 확장을 Ruby와 서드파티 라이브러리에 올바르게 컴파일하고 링크하는 Makefile을 생성하도록 도와주는 모듈이에요.
출처: Ruby 3.3 API
본문
mkmf.rb는 Ruby C 확장이 C 확장을 Ruby와 서드파티 라이브러리에 올바르게 컴파일하고 링크하는 Makefile을 생성하는 데 사용돼요.
Constants (상수)
ASSEMBLE_C— 생성된 Makefile에서 C 파일을 어셈블러 소스로 변환하는 명령.ASSEMBLE_CXX— 생성된 Makefile에서 C++ 파일을 어셈블러 소스로 변환하는 명령.CLEANINGS— 확장 빌드 디렉터리를 정리하는 Makefile 규칙.COMMON_HEADERS— Ruby C 확장의 공통 헤더.COMMON_LIBS— Ruby C 확장의 공통 라이브러리.COMPILE_C— 생성된 Makefile에서 C 파일을 컴파일하는 명령.COMPILE_CXX— 생성된 Makefile에서 C++ 파일을 컴파일하는 명령.COMPILE_RULES— make 컴파일 규칙.CONFIG— Ruby가 빌드될 때의 기본값을 사용하는 makefile 설정.CONFTEST_CXXCXX_EXT— C++ 컴파일러로 컴파일되는 파일의 확장자.C_EXT— C 컴파일러로 컴파일되는 파일의 확장자.EXPORT_PREFIXHDR_EXT— 헤더 파일의 확장자.LIBARG— 링커에 라이브러리를 추가하는 인자.LIBPATHFLAG— 링커에 라이브러리 경로를 추가하는 인자.LINK_SO— 공유 라이브러리를 링크하는 명령.MAIN_DOES_NOTHING— 아무 일도 하지 않는 C main 함수.ORIG_LIBPATHRPATHFLAGRULE_SUBSTSRC_EXT— 소스 파일의 확장자.TRY_LINK— 라이브러리 링크를 테스트하기 위해 프로그램을 컴파일하는 명령.TRY_LINK_CXXUNIVERSAL_INTS
Private Class Methods
[] (name)
[]= (name, mod)
Public Instance Methods
append_cflags (flags, *opts)
주어진 각 C 컴파일러 플래그가 수용 가능한지 검사하고, 그렇다면 $CFLAGS에 추가해요. flags는 C 컴파일러 플래그 하나를 담은 String이거나 그 배열이에요.
check_signedness (type, headers = nil, opts = nil, &b)
주어진 type의 부호성(signedness)을 돌려줘요. 타입을 검색할 추가 헤더를 선택 사항으로 지정할 수 있어요.
타입이 발견되고 숫자 타입이면, 타입 이름을 대문자로 하고 SIGNEDNESS_OF_를 앞에 붙인 형태의 매크로가 전처리기 상수로 컴파일러에 전달돼요. 타입 이름 뒤에 =X가 붙는데, X는 타입이 unsigned면 양의 정수, signed면 음의 정수예요.
예를 들어 size_t가 unsigned로 정의되어 있으면 check_signedness('size_t')는 +1을 돌려주고 SIGNEDNESS_OF_SIZE_T=+1 전처리기 매크로가 컴파일러에 전달돼요. check_signedness('int')에는 SIGNEDNESS_OF_INT=-1 매크로가 설정돼요.
check_sizeof (type, headers = nil, opts = "", &b)
주어진 type의 크기를 돌려줘요. 타입을 검색할 추가 헤더를 선택 사항으로 지정할 수 있어요.
찾으면, 타입 이름을 대문자로 하고 SIZEOF_를 앞에 붙인 매크로가 전처리기 상수로 컴파일러에 전달돼요. 타입 이름 뒤에 =X가 붙는데, X는 실제 크기예요.
예를 들어 check_sizeof('mystruct')가 12를 돌려주면 SIZEOF_MYSTRUCT=12 전처리기 매크로가 컴파일러에 전달돼요.
convertible_int (type, headers = nil, opts = nil, &b)
주어진 type의 변환 가능한(convertible) 정수 타입을 돌려줘요. 추가 헤더를 선택 사항으로 지정할 수 있어요. convertible이라는 건 실제로 같은 타입이거나 같은 타입에서 typedef된 것이라는 뜻이에요.
타입이 정수 타입이고 변환 가능한 타입이 발견되면, 다음 매크로들이 타입 이름을 대문자로 하여 전처리기 상수로 컴파일러에 전달돼요.
TYPEOF_뒤에 타입 이름, 뒤에=X—X는 발견된 변환 가능한 타입 이름.TYP2NUM과NUM2TYP— 여기서TYP는_t접미사를T로 바꾼 대문자 타입 이름, 뒤에=X—X는 타입을 Integer 객체로 변환하는 매크로 이름(및 그 반대).
예를 들어 foobar_t가 unsigned long으로 정의되어 있으면 convertible_int("foobar_t")는 "unsigned long"을 돌려주고 다음 매크로를 정의해요.
#define TYPEOF_FOOBAR_T unsigned long
#define FOOBART2NUM ULONG2NUM
#define NUM2FOOBART NUM2ULONG
create_header (header = "extconf.h")
have_func, have_header 같은 다른 메서드들이 생성한 다양한 매크로 정의로 이뤄진 헤더 파일을 생성해요. 이것들은 헤더 파일 이름(기본값 "extconf.h")에 기반한 커스텀 #ifndef로 감싸져요.
예시:
# extconf.rb
require 'mkmf'
have_func('realpath')
have_header('sys/utime.h')
create_header
create_makefile('foo')
위 스크립트는 다음 extconf.h 파일을 생성해요.
#ifndef EXTCONF_H
#define EXTCONF_H
#define HAVE_REALPATH 1
#define HAVE_SYS_UTIME_H 1
#endif
create_header 메서드는 extconf.rb 파일에서 앞서 설정된 정의에 기반해 파일을 생성하므로, 스크립트에서 마지막에 호출하는 메서드 중 하나로 만드는 게 좋아요.
create_makefile (target, srcprefix = nil) { |conf| ... }
확장용 Makefile을 생성하고, 다른 메서드들을 통해 생성했을 수 있는 옵션과 전처리기 상수를 전달해요.
target 이름은 C 확장 안에 정의된 전역 함수 이름에서 Init_를 뺀 것과 일치해야 해요. 예를 들어 C 확장이 Init_foo로 정의되어 있으면 target은 그냥 "foo"가 돼요.
target 이름에 / 문자가 있으면 마지막 이름만 target 이름으로 해석되고, 나머지는 최상위 디렉터리 이름으로 간주돼요. 생성된 Makefile은 그 디렉터리 구조를 따르도록 알맞게 변경돼요. 예를 들어 target 이름으로 "test/foo"를 전달하면 확장이 "test" 디렉터리 아래에 설치돼요. 즉 나중에 Ruby 프로그램에서 그 파일을 로드하려면 그 디렉터리 구조를 따라야 해요. 예: require 'test/foo'.
srcprefix는 소스 파일이 빌드 스크립트와 같은 디렉터리에 없을 때 사용해야 해요. 이렇게 하면 소스 파일을 빌드 스크립트와 같은 디렉터리에 수동으로 복사할 필요가 없어질 뿐 아니라, 생성된 Makefile에 적절한 target_prefix를 설정해 줘요.
target_prefix를 설정하면, make install을 실행할 때 생성된 바이너리가 RbConfig::CONFIG['sitearchdir'] 아래의 로컬 파일 시스템을 본뜬 디렉터리에 설치돼요.
예를 들어 다음 파일 트리가 있고:
ext/
extconf.rb
test/
foo.c
다음 코드가 주어지면:
create_makefile('test/foo', 'test')
생성된 Makefile의 target_prefix가 "test"로 설정돼요. 그 결과 make install 명령으로 설치할 때 다음 파일 트리가 만들어져요.
/path/to/ruby/sitearchdir/test/foo.so
수동으로 파일을 복사하는 대신 이 접근 방식으로 makefile을 생성하는 걸 권장해요. 어떤 서드파티 라이브러리는 target_prefix가 제대로 설정되는 것에 의존할 수 있으니까요.
srcprefix 인자는 기본 소스 디렉터리(즉 현재 디렉터리)를 덮어쓰는 데 쓸 수 있어요. 이것은 VPATH의 일부로 포함되고 INCFLAGS 목록에 추가돼요.
depend_rules (depend)
"depend" 파일의 데이터 내용을 처리해요. 이 파일의 각 줄은 파일 이름일 것으로 기대돼요. 발견한 결과를 Makefile 형식으로 돌려줘요.
dir_config(target)
사용자가 커맨드라인에서 그 이름으로 다양한 "with" 옵션을 설정하는 데 쓸 수 있는 target 이름을 설정해요. 예를 들어 target이 "foo"로 설정되면, 사용자는 --with-foo-dir=prefix, --with-foo-include=dir, --with-foo-lib=dir 커맨드라인 옵션으로 헤더/라이브러리 파일을 어디에서 찾을지 알려줄 수 있어요.
기본값을 지정하려면 추가 매개변수를 전달할 수 있어요. 하나가 주어지면 기본 prefix로, 두 개가 주어지면 순서대로 "include"와 "lib" 기본값으로 취급돼요.
어쨌든 반환값은 결정된 "include"와 "lib" 디렉터리의 배열이에요. 대응하는 커맨드라인 옵션이 주어지지 않고 기본값도 지정되지 않으면 둘 다 nil일 수 있어요.
dir_config는 라이브러리와 include 파일을 검색할 장소 목록에만 추가한다는 점을 기억하세요. 라이브러리를 애플리케이션에 링크하지 않아요.
dummy_makefile (srcdir)
스텁(stub) Makefile을 만들어요.
enable_config (config, default=nil) { |config, default| ... }
--enable-config 또는 --disable-config 옵션의 존재를 검사해요. enable 옵션이 주어지면 true, disable 옵션이 주어지면 false, 그 외에는 기본값을 돌려줘요.
디버그 정보 같은 커스텀 정의를 추가하는 데 유용할 수 있어요. 예시:
if enable_config("debug")
$defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG"
end
find_executable (bin, path = nil)
path에서 실행 파일 bin을 검색해요. 기본 경로는 PATH 환경 변수예요. 그게 정의되어 있지 않으면 /usr/local/bin, /usr/ucb, /usr/bin, /bin을 검색해요.
찾으면 발견한 곳의 전체 경로(실행 파일 이름 포함)를 돌려줘요. 이 메서드가 생성된 Makefile에는 실제로 영향을 주지 않는다는 점을 기억하세요.
find_header (header, *paths)
mkmf에게 주어진 paths 중 아무 경로에서든 주어진 header를 검색하라고 지시하고, 그 경로들에서 찾았는지 여부를 돌려줘요. header를 찾으면 발견된 경로가 (via -I 스위치) 컴파일러에 보내지는 include 디렉터리 목록에 추가돼요.
find_library (lib, func, *paths, &b)
지정된 paths 중 하나의 라이브러리 lib 안에서 진입점 func를 찾을 수 있는지 여부를 돌려줘요. paths는 문자열 배열이에요. func가 nil이면 main() 함수가 진입점으로 사용돼요. lib를 찾으면 발견된 경로가 검색·링크되는 라이브러리 경로 목록에 추가돼요.
find_type (type, opt, *headers, &b)
정적 타입 type이 어디에 정의되어 있는지를 돌려줘요. opt에 추가 플래그를 전달할 수도 있고, 그 플래그들은 컴파일러에 전달돼요. have_type도 참고하세요.
have_const (const, headers = nil, opt = "", &b)
상수 const가 정의되어 있는지 여부를 돌려줘요. const의 타입을 [const, type]으로 선택 사항으로 전달할 수 있어요. 예:
have_const(%w[PTHREAD_MUTEX_INITIALIZER pthread_mutex_t], "pthread.h")
공통 헤더 파일 외에 검사할 추가 헤더와, 컴파일러에 전달될 opt에 대한 추가 플래그를 전달할 수도 있어요.
찾으면, 타입 이름을 대문자로 하고 HAVE_CONST_를 앞에 붙인 매크로가 전처리기 상수로 컴파일러에 전달돼요. 예를 들어 have_const('foo')가 true를 돌려주면 HAVE_CONST_FOO 전처리기 매크로가 컴파일러에 전달돼요.
have_framework (fw, &b)
주어진 프레임워크(framework)를 시스템에서 찾을 수 있는지 여부를 돌려줘요. 찾으면, 프레임워크 이름을 대문자로 하고 HAVE_FRAMEWORK_를 앞에 붙인 매크로가 전처리기 상수로 컴파일러에 전달돼요.
예를 들어 have_framework('Ruby')가 true를 돌려주면 HAVE_FRAMEWORK_RUBY 전처리기 매크로가 컴파일러에 전달돼요. fw가 프레임워크 이름과 헤더 파일 이름의 쌍이면, 보통 프레임워크와 같은 이름으로 명명된 기본 헤더 파일 대신 그 헤더 파일이 검사돼요.
have_func (func, headers = nil, opt = "", &b)
함수 func를 공통 헤더 파일이나 제공한 헤더 안에서 찾을 수 있는지 여부를 돌려줘요. 찾으면, 함수 이름을 대문자로 하고 HAVE_를 앞에 붙인 매크로가 전처리기 상수로 컴파일러에 전달돼요.
추가 라이브러리의 함수를 검사하려면 먼저 have_library()로 그 라이브러리를 검사해야 해요. func는 함수 이름만이거나 인자가 있는 함수 이름이어야 해요. 예를 들어 have_func('foo')가 true를 돌려주면 HAVE_FOO 전처리기 매크로가 컴파일러에 전달돼요.
have_header (header, preheaders = nil, opt = "", &b)
주어진 헤더 파일을 시스템에서 찾을 수 있는지 여부를 돌려줘요. 찾으면, 헤더 파일 이름을 대문자로 하고 HAVE_를 앞에 붙인 매크로가 전처리기 상수로 컴파일러에 전달돼요. 예를 들어 have_header('foo.h')가 true를 돌려주면 HAVE_FOO_H 전처리기 매크로가 컴파일러에 전달돼요.
have_library (lib, func = nil, headers = nil, opt = "", &b)
주어진 진입점 func를 lib 안에서 찾을 수 있는지 여부를 돌려줘요. func가 nil이면 기본적으로 main() 진입점이 사용돼요. 찾으면, 확장을 링크할 때 사용할 라이브러리 목록에 그 라이브러리를 추가해요.
headers가 제공되면 func를 검색할 때 찾는 헤더 파일로 그 헤더 파일들을 포함해요. 링크될 라이브러리의 실제 이름은 --with-FOOlib 설정 옵션으로 바꿀 수 있어요.
have_macro (macro, headers = nil, opt = "", &b)
macro가 공통 헤더 파일이나 제공한 헤더 안에서 정의되어 있는지 여부를 돌려줘요. opt에 전달하는 어떤 옵션이든 컴파일러에 전달돼요.
have_struct_member (type, member, headers = nil, opt = "", &b)
타입 type의 struct가 member를 포함하는지 여부를 돌려줘요. 포함하지 않거나 struct 타입을 찾을 수 없으면 false를 돌려줘요. (공통 헤더 파일 외에) struct를 검색할 추가 헤더를 선택 사항으로 지정할 수 있어요.
찾으면, 타입 이름과 멤버 이름을 대문자로 하고 HAVE_를 앞에 붙인 매크로가 전처리기 상수로 컴파일러에 전달돼요. 예를 들어 have_struct_member('struct foo', 'bar')가 true를 돌려주면 HAVE_STRUCT_FOO_BAR 전처리기 매크로가 컴파일러에 전달돼요. 역호환을 위해 HAVE_ST_BAR도 정의돼요.
have_type (type, headers = nil, opt = "", &b)
정적 타입 type이 정의되어 있는지 여부를 돌려줘요. (공통 헤더 파일 외에) 검사할 추가 헤더를 선택 사항으로 전달할 수 있어요. opt에 추가 플래그를 전달할 수도 있고, 그 플래그들은 컴파일러에 전달돼요.
찾으면, 타입 이름을 대문자로 하고 HAVE_TYPE_를 앞에 붙인 매크로가 전처리기 상수로 컴파일러에 전달돼요. 예를 들어 have_type('foo')가 true를 돌려주면 HAVE_TYPE_FOO 전처리기 매크로가 컴파일러에 전달돼요.
have_var (var, headers = nil, opt = "", &b)
변수 var를 공통 헤더 파일이나 제공한 헤더 안에서 찾을 수 있는지 여부를 돌려줘요. 찾으면, 변수 이름을 대문자로 하고 HAVE_를 앞에 붙인 매크로가 전처리기 상수로 컴파일러에 전달돼요.
추가 라이브러리의 변수를 검사하려면 먼저 have_library()로 그 라이브러리를 검사해야 해요. 예를 들어 have_var('foo')가 true를 돌려주면 HAVE_FOO 전처리기 매크로가 컴파일러에 전달돼요.
pkg_config (pkg, *options)
설치된 라이브러리에 대한 컴파일/링크 정보를 [cflags, ldflags, libs] 튜플로 돌려줘요. 다음 명령들 중 가장 먼저 발견되는 명령을 사용해요.
- 커맨드라인 옵션으로
--with-{pkg}-config={command}가 주어지면:{command} {options} {pkg}-config {options}pkg-config {options} {pkg}
여기서 options는 대시가 없는 옵션 이름이에요. 예를 들어 --cflags 플래그에 "cflags". 얻은 값들은 $INCFLAGS, $CFLAGS, $LDFLAGS, $libs에 추가돼요.
options 인자가 하나 이상 주어지면, 설정 명령이 그 옵션과 함께 호출되고 위에 언급한 전역 값들을 수정하지 않고 공백이 제거된 출력 문자열을 돌려줘요.
try_const (const, headers = nil, opt = "", &b)
상수 const가 정의되어 있는지 여부를 돌려줘요. have_const도 참고하세요.
try_type (type, headers = nil, opt = "", &b)
정적 타입 type이 정의되어 있는지 여부를 돌려줘요. have_type도 참고하세요.
with_config (config, default=nil) { |config, default| ... }
--with-config 또는 --without-config 옵션의 존재를 검사해요. with 옵션이 주어지면 true, without 옵션이 주어지면 false, 그 외에는 기본값을 돌려줘요.
디버그 정보 같은 커스텀 정의를 추가하는 데 유용할 수 있어요. 예시:
if with_config("debug")
$defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG"
end