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_CXX
  • CXX_EXT — C++ 컴파일러로 컴파일되는 파일의 확장자.
  • C_EXT — C 컴파일러로 컴파일되는 파일의 확장자.
  • EXPORT_PREFIX
  • HDR_EXT — 헤더 파일의 확장자.
  • LIBARG — 링커에 라이브러리를 추가하는 인자.
  • LIBPATHFLAG — 링커에 라이브러리 경로를 추가하는 인자.
  • LINK_SO — 공유 라이브러리를 링크하는 명령.
  • MAIN_DOES_NOTHING — 아무 일도 하지 않는 C main 함수.
  • ORIG_LIBPATH
  • RPATHFLAG
  • RULE_SUBST
  • SRC_EXT — 소스 파일의 확장자.
  • TRY_LINK — 라이브러리 링크를 테스트하기 위해 프로그램을 컴파일하는 명령.
  • TRY_LINK_CXX
  • UNIVERSAL_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_ 뒤에 타입 이름, 뒤에 =XX는 발견된 변환 가능한 타입 이름.
  • TYP2NUMNUM2TYP — 여기서 TYP_t 접미사를 T로 바꾼 대문자 타입 이름, 뒤에 =XX는 타입을 Integer 객체로 변환하는 매크로 이름(및 그 반대).

예를 들어 foobar_tunsigned 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는 문자열 배열이에요. funcnil이면 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)

주어진 진입점 funclib 안에서 찾을 수 있는지 여부를 돌려줘요. funcnil이면 기본적으로 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

Private Instance Methods

cc_command (opt="")

conftest_source ()

have_devel? ()

link_command (ldflags, *opts)