caffe 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +110 -0
- data/ext/caffe/blob.cc +145 -0
- data/ext/caffe/blob.hpp +32 -0
- data/ext/caffe/caffe.cc +13 -0
- data/ext/caffe/common.cc +36 -0
- data/ext/caffe/common.hpp +17 -0
- data/ext/caffe/extconf.rb +39 -0
- data/ext/caffe/mkmf_cxx.rb +158 -0
- data/ext/caffe/net.cc +55 -0
- data/ext/caffe/net.hpp +10 -0
- data/ext/caffe/util.hpp +39 -0
- data/lib/caffe.rb +5 -0
- data/lib/caffe/blob.rb +35 -0
- data/lib/caffe/caffe.pb.rb +902 -0
- data/lib/caffe/common.rb +6 -0
- data/lib/caffe/version.rb +3 -0
- data/spec/blob_spec.rb +104 -0
- data/spec/common_spec.rb +26 -0
- data/spec/net/gen_data.rb +29 -0
- data/spec/net/test.caffemodel +0 -0
- data/spec/net/test.prototxt +71 -0
- data/spec/net/test_solver.prototxt +14 -0
- data/spec/net/test_train.prototxt +83 -0
- data/spec/net_spec.rb +71 -0
- data/spec/spec_helper.rb +16 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 87a24cb6239268eff1a716f0f770edf6b708d0f3
|
4
|
+
data.tar.gz: 4e589a9b416d7191567d6d07bc7b127d6bded9d4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 768510fe5634d31bd847593679836ee2b123264693ce3698dd421c30129fa76b7c26194939669e4e55c904c5def2825b5941a9cafec8df0cc2f756a9d146ac15
|
7
|
+
data.tar.gz: b3a6c3de6efe2190be72ac7637c81f300182b7c5b4417fedbfff28eaf42a5f02eab71dc00c17aadf752f104ad60ec1a6110f983d1c78f786a1b1e8f9629da2cb
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Tiny Tiny
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Ruby Caffe #
|
2
|
+
|
3
|
+
A ruby wrapper for the deep learning framework
|
4
|
+
|
5
|
+
## Linking caffe ##
|
6
|
+
|
7
|
+
There is a linking problem since `caffe` doesn't provide an official installation plan, but everything in its build path / distribute path instead of somewhere like `/usr` or `/usr/local`
|
8
|
+
|
9
|
+
Now `extconf.rb` only checks for `cblas.h` (or `mkl.h` if you are using Intel MKL), `-lcaffe` and `caffe/caffe.hpp`, other dependent headers of `caffe` (such as `leveldb`) should be installed in default search path (`/usr/include` or `/usr/local/include`), or you can use `pkg-config` to add additional options in `LDFLAGS` & `CFLAGS` directly (see below)
|
10
|
+
|
11
|
+
There are three options to specify the path of the libraries & headers:
|
12
|
+
|
13
|
+
### `pkg-config` ###
|
14
|
+
|
15
|
+
Ruby `mkmf` provide convenient use of `pkg-config`, which is included in `extconf.rb`. It will search for `caffe.pc` in `pkg-config` search paths (typically `/usr/lib/pkgconfig` and `/usr/local/lib/pkgconfig`)
|
16
|
+
|
17
|
+
However `caffe` doesn't provide a `.pc` file, but you can write one yourself, here is an example:
|
18
|
+
|
19
|
+
```
|
20
|
+
prefix=/usr/local/caffe
|
21
|
+
libdir=${prefix}/lib
|
22
|
+
includedir=${prefix}/include
|
23
|
+
blasinclude=/usr/local/openblas/include
|
24
|
+
otherflags=-DCPU_ONLY
|
25
|
+
|
26
|
+
Name: caffe
|
27
|
+
Description: caffe C++ lib
|
28
|
+
Version: 1.0.0-rc4
|
29
|
+
Libs: -L${libdir} -lcaffe
|
30
|
+
CFlags: ${otherflags} -I${includedir} -I${blasinclude}
|
31
|
+
```
|
32
|
+
|
33
|
+
Note that `pkg-config` directly adds `LDFLAGS` & `CFLAGS`, so you can add flags other than what `extconf.rb` specifies
|
34
|
+
|
35
|
+
### Build Flags ###
|
36
|
+
|
37
|
+
Build flags can be passed to `extconf.rb` to specify the `caffe` path and other configuration:
|
38
|
+
|
39
|
+
- `--with-caffe-dir=<path>`: specify the caffe path, in which `<path>/include` contains the headers and `<path>/lib` contains the library
|
40
|
+
- `--with-caffe-include` & `--with-caffe-lib`: specify the caffe header and lib path separately
|
41
|
+
- `--with-blas-include=<path>`: specify the path that contains the blas headers
|
42
|
+
- `--with-blas-dir=<path>`: the same as `--with-blas-include=<path>/include`
|
43
|
+
- `--enable-gpu` & `--disable-gpu`: specify GPU mode of caffe, enabled by default
|
44
|
+
- `--enable-mkl` & `--disable-mkl`: specify if Intel MKL is used as blas library, if neither flags are set `extconf.rb` will search for `cblas.h` first and then `mkl.h` if fails
|
45
|
+
|
46
|
+
If `rake` is used to build the extension, use:
|
47
|
+
|
48
|
+
```shell
|
49
|
+
$ rake compile -- <build flags>
|
50
|
+
```
|
51
|
+
|
52
|
+
If installing from `gem`, use:
|
53
|
+
|
54
|
+
```shell
|
55
|
+
$ gem install caffe -- <build flags>
|
56
|
+
```
|
57
|
+
|
58
|
+
If installing from `bundler`, use:
|
59
|
+
|
60
|
+
```shell
|
61
|
+
$ bundle config build.caffe <build flags>
|
62
|
+
|
63
|
+
$ bundle install
|
64
|
+
```
|
65
|
+
|
66
|
+
### Put everything in search path ###
|
67
|
+
|
68
|
+
If you put all the headers and libs required in default search path (like `/usr/local` & `/usr`), and use the default setting (with GPU mode) then everything should be ok
|
69
|
+
|
70
|
+
## Build ##
|
71
|
+
|
72
|
+
Now the project is not a gem project, so it can be built by `rake`
|
73
|
+
|
74
|
+
First, use `bundler` to install all the dependencies:
|
75
|
+
|
76
|
+
```shell
|
77
|
+
$ bundle install
|
78
|
+
```
|
79
|
+
|
80
|
+
Then, use `rake` to build:
|
81
|
+
|
82
|
+
```shell
|
83
|
+
$ rake compile -- <build flags>
|
84
|
+
```
|
85
|
+
|
86
|
+
Test with:
|
87
|
+
|
88
|
+
```shell
|
89
|
+
$ rake test
|
90
|
+
```
|
91
|
+
|
92
|
+
or after compilation:
|
93
|
+
|
94
|
+
```shell
|
95
|
+
$ rake spec
|
96
|
+
```
|
97
|
+
|
98
|
+
require the lib with:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
require './lib/caffe'
|
102
|
+
```
|
103
|
+
|
104
|
+
## Author ##
|
105
|
+
|
106
|
+
Tiny Tiny
|
107
|
+
|
108
|
+
## License ##
|
109
|
+
|
110
|
+
MIT
|
data/ext/caffe/blob.cc
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
#include "blob.hpp"
|
2
|
+
#include "util.hpp"
|
3
|
+
#include <rice/Data_Type.hpp>
|
4
|
+
#include <rice/Constructor.hpp>
|
5
|
+
#include <rice/Module.hpp>
|
6
|
+
#include <stdexcept>
|
7
|
+
#include <algorithm>
|
8
|
+
|
9
|
+
using namespace Rice;
|
10
|
+
|
11
|
+
BlobCursor::BlobCursor(Object blob, bool diff) : diff(diff) {
|
12
|
+
ref = from_ruby<Blob *>(blob);
|
13
|
+
}
|
14
|
+
|
15
|
+
void BlobCursor::checkValid() const {
|
16
|
+
int n = indices.size();
|
17
|
+
const std::vector<int> &shape = ref -> shape();
|
18
|
+
if (n > shape.size()) throw std::logic_error("Invalid BlobCursor!");
|
19
|
+
for (int i = 0; i < n; ++i) {
|
20
|
+
if (shape[i] <= indices[i]) {
|
21
|
+
throw std::out_of_range("Index out of range!");
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
BlobCursor BlobCursor::next(int index) const {
|
27
|
+
BlobCursor ret = *this;
|
28
|
+
ret.indices.push_back(index);
|
29
|
+
ret.checkValid();
|
30
|
+
return ret;
|
31
|
+
}
|
32
|
+
|
33
|
+
Object BlobCursor::get(int index) const {
|
34
|
+
BlobCursor next = this -> next(index);
|
35
|
+
if (next.indices.size() == ref -> shape().size()) {
|
36
|
+
return to_ruby(*next.begin());
|
37
|
+
} else {
|
38
|
+
return to_ruby(next);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
Object BlobCursor::set(int index, Object data) {
|
43
|
+
BlobCursor next = this -> next(index);
|
44
|
+
if (next.indices.size() == ref -> shape().size()) {
|
45
|
+
*next.mbegin() = from_ruby<float>(data);
|
46
|
+
return data;
|
47
|
+
} else {
|
48
|
+
throw std::logic_error("Not implemented!");
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
const float *BlobCursor::begin() {
|
53
|
+
std::vector<int> full = indices;
|
54
|
+
full.resize(ref -> shape().size());
|
55
|
+
const float *base = diff ? ref -> cpu_diff() : ref -> cpu_data();
|
56
|
+
|
57
|
+
return base + ref -> offset(full);
|
58
|
+
}
|
59
|
+
|
60
|
+
const float *BlobCursor::end() {
|
61
|
+
return begin() + count();
|
62
|
+
}
|
63
|
+
|
64
|
+
float *BlobCursor::mbegin() {
|
65
|
+
std::vector<int> full = indices;
|
66
|
+
full.resize(ref -> shape().size());
|
67
|
+
float *base = diff ? ref -> mutable_cpu_diff() : ref -> mutable_cpu_data();
|
68
|
+
|
69
|
+
return base + ref -> offset(full);
|
70
|
+
}
|
71
|
+
|
72
|
+
float *BlobCursor::mend() {
|
73
|
+
return mbegin() + count();
|
74
|
+
}
|
75
|
+
|
76
|
+
int BlobCursor::count() const {
|
77
|
+
const std::vector<int> &shape = ref -> shape();
|
78
|
+
int n = shape.size();
|
79
|
+
int ans = 1;
|
80
|
+
for (int k = indices.size(); k < n; ++k) {
|
81
|
+
ans *= shape[k];
|
82
|
+
}
|
83
|
+
return ans;
|
84
|
+
}
|
85
|
+
|
86
|
+
int BlobCursor::copy(Array data) {
|
87
|
+
int n = std::min(int(data.size()), count());
|
88
|
+
float *base = mbegin();
|
89
|
+
for (int i = 0; i < n; ++i) {
|
90
|
+
base[i] = from_ruby<float>(data[i]);
|
91
|
+
}
|
92
|
+
return n;
|
93
|
+
}
|
94
|
+
|
95
|
+
static Object getBlobData(Object self) {
|
96
|
+
return to_ruby(BlobCursor(self));
|
97
|
+
}
|
98
|
+
|
99
|
+
static Object getBlobDiff(Object self) {
|
100
|
+
return to_ruby(BlobCursor(self, true));
|
101
|
+
}
|
102
|
+
|
103
|
+
static Array getBlobShape(Object self) {
|
104
|
+
const std::vector<int> &shape = from_ruby<Blob *>(self) -> shape();
|
105
|
+
return Array(shape.begin(), shape.end());
|
106
|
+
}
|
107
|
+
|
108
|
+
static Array setBlobShape(Object self, Array shape) {
|
109
|
+
if (shape.size() > 4) {
|
110
|
+
throw std::invalid_argument("Shape dimension exceeds 4!");
|
111
|
+
}
|
112
|
+
Blob *blob = from_ruby<Blob *>(self);
|
113
|
+
blob -> Reshape(arrayToVector<int>(shape));
|
114
|
+
return shape;
|
115
|
+
}
|
116
|
+
|
117
|
+
struct BlobConstructor {
|
118
|
+
static void construct(Object self, Array shape) {
|
119
|
+
if (shape.size() > 4) {
|
120
|
+
throw std::invalid_argument("Shape dimension exceeds 4!");
|
121
|
+
}
|
122
|
+
DATA_PTR(self.value()) = new Blob(arrayToVector<int>(shape));
|
123
|
+
}
|
124
|
+
};
|
125
|
+
|
126
|
+
void Init_blob() {
|
127
|
+
Module rb_mCaffe = define_module("Caffe");
|
128
|
+
|
129
|
+
Data_Type<Blob> rb_cBlob = rb_mCaffe
|
130
|
+
.define_class<Blob>("Blob")
|
131
|
+
.define_constructor(BlobConstructor())
|
132
|
+
.define_method("data", getBlobData)
|
133
|
+
.define_method("diff", getBlobDiff)
|
134
|
+
.define_method("shape", getBlobShape)
|
135
|
+
.define_method("shape=", setBlobShape);
|
136
|
+
|
137
|
+
Data_Type<BlobCursor> rb_cCursor = rb_cBlob
|
138
|
+
.define_class<BlobCursor>("Cursor")
|
139
|
+
.define_constructor(Constructor<BlobCursor, Object, bool>())
|
140
|
+
.define_method("[]", &BlobCursor::get)
|
141
|
+
.define_method("[]=", &BlobCursor::set)
|
142
|
+
.define_method("size", &BlobCursor::count)
|
143
|
+
.define_iterator(&BlobCursor::begin, &BlobCursor::end)
|
144
|
+
.define_method("copy_from!", &BlobCursor::copy);
|
145
|
+
}
|
data/ext/caffe/blob.hpp
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#ifndef __BLOB
|
2
|
+
#define __BLOB
|
3
|
+
|
4
|
+
#include <rice/Class.hpp>
|
5
|
+
#include <rice/Array.hpp>
|
6
|
+
#include <caffe/caffe.hpp>
|
7
|
+
#include <vector>
|
8
|
+
#include <stdexcept>
|
9
|
+
|
10
|
+
typedef caffe::Blob<float> Blob;
|
11
|
+
|
12
|
+
class BlobCursor {
|
13
|
+
std::vector<int> indices;
|
14
|
+
Blob *ref;
|
15
|
+
bool diff;
|
16
|
+
void checkValid(void) const;
|
17
|
+
public:
|
18
|
+
BlobCursor(Rice::Object blob, bool diff = false);
|
19
|
+
BlobCursor next(int index) const;
|
20
|
+
Rice::Object get(int index) const;
|
21
|
+
Rice::Object set(int index, Rice::Object data);
|
22
|
+
const float *begin(void);
|
23
|
+
const float *end(void);
|
24
|
+
float *mbegin(void);
|
25
|
+
float *mend(void);
|
26
|
+
int count(void) const;
|
27
|
+
int copy(Rice::Array data);
|
28
|
+
};
|
29
|
+
|
30
|
+
void Init_blob(void);
|
31
|
+
|
32
|
+
#endif
|
data/ext/caffe/caffe.cc
ADDED
data/ext/caffe/common.cc
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#include "common.hpp"
|
2
|
+
#include <rice/Module.hpp>
|
3
|
+
#include <rice/Class.hpp>
|
4
|
+
#include <caffe/caffe.hpp>
|
5
|
+
|
6
|
+
using namespace Rice;
|
7
|
+
|
8
|
+
#define EnumCast(type, classname) template<> \
|
9
|
+
type from_ruby<type>(Object self) { \
|
10
|
+
int num = from_ruby<int>(self.call("to_i")); \
|
11
|
+
return type(num); \
|
12
|
+
} \
|
13
|
+
template<> \
|
14
|
+
Object to_ruby<type>(const type &self) { \
|
15
|
+
int num = int(self); \
|
16
|
+
Object cl = (classname); \
|
17
|
+
return cl.call("fetch", num); \
|
18
|
+
}
|
19
|
+
|
20
|
+
EnumCast(caffe::Phase, define_module("Caffe")
|
21
|
+
.const_get("Phase"));
|
22
|
+
EnumCast(caffe::Caffe::Brew, Class(define_module("Caffe")
|
23
|
+
.const_get("SolverParameter"))
|
24
|
+
.const_get("SolverMode"));
|
25
|
+
|
26
|
+
void Init_common() {
|
27
|
+
Module rb_mCaffe = define_module("Caffe")
|
28
|
+
.define_module_function("mode", caffe::Caffe::mode)
|
29
|
+
.define_module_function("mode=", caffe::Caffe::set_mode)
|
30
|
+
.define_module_function("solver_count", caffe::Caffe::solver_count)
|
31
|
+
.define_module_function("solver_count=", caffe::Caffe::set_solver_count)
|
32
|
+
.define_module_function("solver_rank", caffe::Caffe::solver_rank)
|
33
|
+
.define_module_function("solver_rank=", caffe::Caffe::set_solver_rank)
|
34
|
+
.define_module_function("multiprocess", caffe::Caffe::multiprocess)
|
35
|
+
.define_module_function("multiprocess=", caffe::Caffe::set_multiprocess);
|
36
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#ifndef __COMMON
|
2
|
+
#define __COMMON
|
3
|
+
|
4
|
+
#include <caffe/caffe.hpp>
|
5
|
+
#include <rice/Data_Type.hpp>
|
6
|
+
|
7
|
+
#define DefineEnumCast(type) template<> \
|
8
|
+
type from_ruby<type>(Rice::Object); \
|
9
|
+
template<> \
|
10
|
+
Rice::Object to_ruby<type>(const type &)
|
11
|
+
|
12
|
+
DefineEnumCast(caffe::Phase);
|
13
|
+
DefineEnumCast(caffe::Caffe::Brew);
|
14
|
+
|
15
|
+
void Init_common(void);
|
16
|
+
|
17
|
+
#endif
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path '../mkmf_cxx', __FILE__
|
2
|
+
|
3
|
+
@libdir_basename = 'lib'
|
4
|
+
|
5
|
+
dir_config 'caffe'
|
6
|
+
dir_config 'blas'
|
7
|
+
|
8
|
+
checking_for 'caffe.pc' do
|
9
|
+
pkg_config 'caffe'
|
10
|
+
end
|
11
|
+
|
12
|
+
$libs = ''
|
13
|
+
|
14
|
+
$defs.push '-DCPU_ONLY' unless enable_config 'gpu', true
|
15
|
+
|
16
|
+
mkl = enable_config 'mkl', nil
|
17
|
+
|
18
|
+
if mkl == true || !have_header('cblas.h')
|
19
|
+
$defs.push '-DUSE_MKL'
|
20
|
+
if mkl == false || !have_header('mkl.h')
|
21
|
+
puts <<MSG
|
22
|
+
blas header not found.
|
23
|
+
use build flag "--with-blas-dir=/path/to/blas" to specify the blas path.
|
24
|
+
or use "--with-blas-include" to specify the include path.
|
25
|
+
MSG
|
26
|
+
raise
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
unless library?('caffe') && header_cxx?('caffe/caffe.hpp')
|
31
|
+
puts <<MSG
|
32
|
+
caffe not found.
|
33
|
+
use build flag "--with-caffe-dir=/path/to/caffe" to specify the caffe path.
|
34
|
+
or use "--with-caffe-include" & "--with-caffe-lib" to specify include path & lib path separately.
|
35
|
+
MSG
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
|
39
|
+
create_makefile 'caffe/caffe'
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'mkmf-rice'
|
2
|
+
|
3
|
+
$CXXFLAGS = ''
|
4
|
+
|
5
|
+
TRY_LINK.sub!(/^#{Regexp.quote($CXX)}/, '$(CXX)')
|
6
|
+
$LDSHARED_CXX.sub!(/^#{Regexp.quote($CXX)}/, '$(CXX)')
|
7
|
+
$CXX = '$(CXX)'
|
8
|
+
|
9
|
+
if CONFIG['target_os'] == 'mswin32' && $RICE_USING_MINGW32
|
10
|
+
init_rice_mkmf_cross_compile_mingw2_for_vc6
|
11
|
+
end
|
12
|
+
|
13
|
+
def configuration(srcdir)
|
14
|
+
configuration = configuration_orig(srcdir)
|
15
|
+
configuration.each do |config|
|
16
|
+
# Make sure we link the extension using the C++ compiler
|
17
|
+
config.gsub!(/^LDSHARED\s*=.*$/, "LDSHARED = #{$LDSHARED_CXX}")
|
18
|
+
|
19
|
+
# Make sure set the C++ flags correctly
|
20
|
+
config.gsub!(/^CXXFLAGS\s*=.*$/, "CXXFLAGS = $(CFLAGS) #{$CXXFLAGS}")
|
21
|
+
end
|
22
|
+
configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
RbConfig::CONFIG['CXXPP'] = RbConfig::CONFIG['CXX'] + ' -E'
|
26
|
+
CONFTEST_CC = "#{CONFTEST}.cc".freeze
|
27
|
+
|
28
|
+
def create_tmpsrc_cxx(src)
|
29
|
+
src = "#{COMMON_HEADERS}\n#{src}"
|
30
|
+
src = yield(src) if block_given?
|
31
|
+
src.gsub!(/[ \t]+$/, '')
|
32
|
+
src.gsub!(/\A\n+|^\n+$/, '')
|
33
|
+
src.sub!(/[^\n]\z/, "\\&\n")
|
34
|
+
count = 0
|
35
|
+
begin
|
36
|
+
open(CONFTEST_CC, 'wb') do |cfile|
|
37
|
+
cfile.print src
|
38
|
+
end
|
39
|
+
rescue Errno::EACCES
|
40
|
+
if (count += 1) < 5
|
41
|
+
sleep 0.2
|
42
|
+
retry
|
43
|
+
end
|
44
|
+
end
|
45
|
+
src
|
46
|
+
end
|
47
|
+
|
48
|
+
def try_do_cxx(src, command, *opts, &b)
|
49
|
+
unless have_devel?
|
50
|
+
raise <<MSG
|
51
|
+
The compiler failed to generate an executable file.
|
52
|
+
You have to install development tools first.
|
53
|
+
MSG
|
54
|
+
end
|
55
|
+
begin
|
56
|
+
src = create_tmpsrc_cxx(src, &b)
|
57
|
+
xsystem(command, *opts)
|
58
|
+
ensure
|
59
|
+
log_src(src)
|
60
|
+
MakeMakefile.rm_rf "#{CONFTEST}.dSYM"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def link_command(ldflags, opt = '', libpath = $DEFLIBPATH | $LIBPATH)
|
65
|
+
librubyarg = $extmk ? $LIBRUBYARG_STATIC : '$(LIBRUBYARG)'
|
66
|
+
conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote,
|
67
|
+
'src' => CONFTEST_CC,
|
68
|
+
'arch_hdrdir' => $arch_hdrdir.quote,
|
69
|
+
'top_srcdir' => $top_srcdir.quote,
|
70
|
+
'INCFLAGS' => $INCFLAGS,
|
71
|
+
'CPPFLAGS' => $CPPFLAGS,
|
72
|
+
'CXXFLAGS' => $CXXFLAGS,
|
73
|
+
'CFLAGS' => $CFLAGS,
|
74
|
+
'ARCH_FLAG' => $ARCH_FLAG,
|
75
|
+
'LDFLAGS' => "#{$LDFLAGS} #{ldflags}",
|
76
|
+
'LOCAL_LIBS' => "#{$LOCAL_LIBS} #{$libs}",
|
77
|
+
'LIBS' => "#{librubyarg} #{opt} #{$LIBS}")
|
78
|
+
conf['LIBPATH'] = libpathflag(libpath.map do |s|
|
79
|
+
RbConfig.expand(s.dup, conf)
|
80
|
+
end)
|
81
|
+
RbConfig.expand(TRY_LINK.dup, conf)
|
82
|
+
end
|
83
|
+
|
84
|
+
def try_link0(src, opt = '', *opts, &b)
|
85
|
+
cmd = link_command('', opt)
|
86
|
+
if $universal
|
87
|
+
require 'tmpdir'
|
88
|
+
Dir.mktmpdir('mkmf_', oldtmpdir = ENV['TMPDIR']) do |tmpdir|
|
89
|
+
begin
|
90
|
+
ENV['TMPDIR'] = tmpdir
|
91
|
+
try_do_cxx(src, cmd, *opts, &b)
|
92
|
+
ensure
|
93
|
+
ENV['TMPDIR'] = oldtmpdir
|
94
|
+
end
|
95
|
+
end
|
96
|
+
else
|
97
|
+
try_do_cxx(src, cmd, *opts, &b)
|
98
|
+
end && File.executable?(CONFTEST + $EXEEXT)
|
99
|
+
end
|
100
|
+
|
101
|
+
def cxxpp_command(outfile, opt = '')
|
102
|
+
conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote,
|
103
|
+
'srcdir' => $srcdir.quote,
|
104
|
+
'arch_hdrdir' => $arch_hdrdir.quote,
|
105
|
+
'top_srcdir' => $top_srcdir.quote)
|
106
|
+
if $universal && (arch_flag = conf['ARCH_FLAG']) && !arch_flag.empty?
|
107
|
+
conf['ARCH_FLAG'] = arch_flag.gsub(/(?:\G|\s)-arch\s+\S+/, '')
|
108
|
+
end
|
109
|
+
flags = [$INCFLAGS, $CPPFLAGS, $CFLAGS, opt].join ' '
|
110
|
+
RbConfig.expand("$(CXXPP) #{flags} #{CONFTEST_CC} #{outfile}", conf)
|
111
|
+
end
|
112
|
+
|
113
|
+
def try_cxxpp(src, opt = '', *opts, &b)
|
114
|
+
try_do_cxx(src, cxxpp_command(CPPOUTFILE, opt), *opts, &b) &&
|
115
|
+
File.file?('conftest.i')
|
116
|
+
ensure
|
117
|
+
MakeMakefile.rm_f 'conftest*'
|
118
|
+
end
|
119
|
+
|
120
|
+
def trans_opt(opt)
|
121
|
+
[[:to_str], [:join, ' '], [:to_s]].each do |meth, *args|
|
122
|
+
return opt.send(meth, *args) if opt.respond_to?(meth)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def header_cxx?(header, preheaders = nil, opt = '', &b)
|
127
|
+
opt = "#{trans_opt opt} #{trans_opt $defs}"
|
128
|
+
checking_for header do
|
129
|
+
if try_cxxpp(cpp_include(preheaders) + cpp_include(header), opt, &b)
|
130
|
+
$defs.push(format('-DHAVE_%s', header.tr_cpp))
|
131
|
+
true
|
132
|
+
else
|
133
|
+
false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def try_library(libs, opt = '', &b)
|
139
|
+
opt = "#{trans_opt opt} #{libs}"
|
140
|
+
try_link(MAIN_DOES_NOTHING, opt, &b)
|
141
|
+
end
|
142
|
+
|
143
|
+
def library?(lib, opt = '', &b)
|
144
|
+
lib = with_config(lib + 'lib', lib)
|
145
|
+
checking_for format(LIBARG, lib) do
|
146
|
+
if COMMON_LIBS.include?(lib)
|
147
|
+
true
|
148
|
+
else
|
149
|
+
libs = append_library($libs, lib)
|
150
|
+
if try_library(libs, opt, &b)
|
151
|
+
$libs = libs
|
152
|
+
true
|
153
|
+
else
|
154
|
+
false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|