makeconf 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,171 @@
1
+ # A linker combines multiple object files into a single executable or library file.
2
+ #
3
+ class Linker
4
+
5
+ attr_accessor :output, :objects, :quiet, :shared_library
6
+ attr_reader :path
7
+
8
+ def initialize
9
+ @flags = []
10
+ @objects = []
11
+ @output = nil
12
+ @shared_library = false
13
+ @ldadd = []
14
+ @quiet = false # If true, output will be suppressed
15
+
16
+ # Determine the path to the linker executable
17
+ @path = nil
18
+ #TODO: support using Clang/GCC on windows
19
+ #if vendor == 'Microsoft'
20
+ if Platform.is_windows?
21
+ @path = 'LINK.EXE'
22
+ else
23
+ @path = 'cc' #XXX-FIXME horrible
24
+ end
25
+
26
+ if ENV['CC']
27
+ @path = ENV['CC']
28
+ end
29
+ if ENV['LD']
30
+ @path = ENV['LD']
31
+ end
32
+ end
33
+
34
+ def clone
35
+ Marshal.load(Marshal.dump(self))
36
+ end
37
+
38
+ # Sets the ELF soname to the specified string
39
+ def soname(s)
40
+ unless Platform.is_windows?
41
+ @flags.push ['soname', s]
42
+ end
43
+ end
44
+
45
+ # Add all symbols to the dynamic symbol table (GNU ld only)
46
+ def export_dynamic
47
+ unless Platform.is_windows?
48
+ @flags.push 'export-dynamic'
49
+ end
50
+ end
51
+
52
+ # Override the normal search path for the dynamic linker
53
+ def rpath=(dir)
54
+ if Platform.is_solaris?
55
+ @flags.push ['R', dir]
56
+ elsif Platform.is_linux?
57
+ @flags.push ['-rpath', dir]
58
+ elsif Platform.is_windows?
59
+ # XXX-FIXME Windows does not support the rpath concept
60
+ return
61
+ else
62
+ throw 'Unsupported OS'
63
+ end
64
+ @flags.push ['-L', dir]
65
+ end
66
+
67
+ # Returns the linker flags suitable for passing to the compiler
68
+ def flags
69
+ tok = []
70
+
71
+ # Set the output path
72
+ throw 'Output pathname is required' if @output.nil?
73
+ if Platform.is_windows?
74
+ tok.push "/OUT:\"#{@output}"
75
+ else
76
+ tok.push '-o', @output
77
+ end
78
+
79
+ # Enable shared library output
80
+ if @shared_library
81
+ if Platform.is_windows?
82
+ tok.push '/DLL'
83
+ else
84
+ tok.push '-shared'
85
+ tok.push '-fpic' # TODO: sometimes -fPIC is needed
86
+ end
87
+ end
88
+
89
+ # Assume that we want to link with shared libraries
90
+ # built within this project
91
+ tok.push '-L', '.'
92
+
93
+ @flags.each do |f|
94
+ if f.kind_of?(Array)
95
+ tok.push '-Wl,-' + f[0] + ',' + f[1]
96
+ else
97
+ tok.push '-Wl,-' + f
98
+ end
99
+ end
100
+
101
+ return ' ' + tok.join(' ')
102
+ end
103
+
104
+ def command
105
+ # windows: 'link.exe /DLL /OUT:$@ ' + deps.join(' '))
106
+ # linux: 'cc ' .... (see Compiler::)
107
+ cmd = [ @path, flags, @objects, @ldadd ].flatten.join(' ')
108
+ cmd += Platform.dev_null if @quiet
109
+ log.debug "Linker command = `#{cmd}'"
110
+
111
+ return cmd
112
+ end
113
+
114
+ # Return the command formatted as a Makefile rule
115
+ def rule
116
+ ['$(LD)', flags, '$(LDFLAGS)', @objects, @ldadd, '$(LDADD)'].flatten.join(' ')
117
+ end
118
+
119
+ # Execute the linker command
120
+ def link
121
+ throw 'STUB'
122
+ end
123
+
124
+ def flags=(tok)
125
+ @flags = default_flags
126
+ return if tok.nil?
127
+ if tok.kind_of?(Array)
128
+ @flags.concat tok
129
+ elsif tok.kind_of?(String)
130
+ @flags.concat tok.split(' ') #XXX-broken, will not handle things like '-rpath /foo'
131
+ else
132
+ log.error tok.pretty_inspect
133
+ throw 'Invalid flag type'
134
+ end
135
+ end
136
+
137
+ # Try to determine a usable default set of linker flags
138
+ def default_flags
139
+ ldflags = []
140
+
141
+ # GCC on Solaris 10 produces 32-bit code by default, so add -m64
142
+ # when running in 64-bit mode.
143
+ if Platform.is_solaris? and Platform.word_size == 64
144
+ ldflags.push '-m64'
145
+ ldflags.push '-R/usr/sfw/lib/amd64' if Platform.is_x86?
146
+ end
147
+
148
+ ldflags
149
+ end
150
+
151
+ # Add one or more libraries to the list of files to link
152
+ def library(lib)
153
+ case lib.class.to_s
154
+ when 'Array'
155
+ tok = lib
156
+ when 'String'
157
+ tok = lib.split(' ')
158
+ else
159
+ throw "Invalid value: #{lib.class}"
160
+ end
161
+ @ldadd.concat tok
162
+ end
163
+
164
+ private
165
+
166
+ def log
167
+ Makeconf.logger
168
+ end
169
+
170
+ end
171
+
@@ -0,0 +1,126 @@
1
+ # A Makefile is a collection of targets and rules used to build software.
2
+ #
3
+ class Makefile
4
+
5
+ # Object constructor.
6
+ def initialize
7
+ @vars = {}
8
+ @targets = {}
9
+
10
+ %w[all check clean distclean install uninstall distdir].each do |x|
11
+ add_target(x)
12
+ end
13
+ end
14
+
15
+ # Define a variable within the Makefile
16
+ def define_variable(lval,op,rval)
17
+ throw "invalid arguments" if lval.nil? or op.nil?
18
+ throw "variable `#{lval}' is undefined" if rval.nil?
19
+ @vars[lval] = [ op, rval ]
20
+ end
21
+
22
+ def target(object)
23
+ @targets[object]
24
+ end
25
+
26
+ def merge!(src)
27
+ throw 'invalid argument' unless src.is_a?(Makefile)
28
+ @vars.merge!(src.vars)
29
+ src.targets.each do |k,v|
30
+ if targets.has_key?(k)
31
+ targets[k].merge!(v)
32
+ else
33
+ targets[k] = (v)
34
+ end
35
+ end
36
+ end
37
+
38
+ def add_target(object,depends = [], rules = [])
39
+ if object.kind_of?(Target)
40
+ @targets[object.objs] = object
41
+ else
42
+ @targets[object] = Target.new(object,depends,rules)
43
+ end
44
+ end
45
+
46
+ def add_rule(target, rule)
47
+ add_target(target, [], []) unless @targets.has_key? target
48
+ @targets[target].add_rule(rule)
49
+ end
50
+
51
+ # Add a file to the tarball during 'make dist'
52
+ def distribute(path)
53
+ path = [ path ] if path.kind_of? String
54
+
55
+ path.each do |src|
56
+ # FIXME: support Windows backslashery
57
+ if src =~ /\//
58
+ dst = '$(distdir)/' + File.dirname(src)
59
+ @targets['distdir'].mkdir(dst)
60
+ @targets['distdir'].cp(src, dst)
61
+ else
62
+ @targets['distdir'].cp(src, '$(distdir)')
63
+ end
64
+ end
65
+ end
66
+
67
+ # Add a file to be removed during 'make clean'
68
+ # TODO: optimize by eliminating multiple rm(1) fork/exec
69
+ def clean(path)
70
+ add_rule('clean', Platform.rm(Platform.pathspec(path)))
71
+ end
72
+
73
+ # Add a file to be removed during 'make distclean'
74
+ # TODO: optimize by eliminating multiple rm(1) fork/exec
75
+ def distclean(path)
76
+ add_rule('distclean', Platform.rm(Platform.pathspec(path)))
77
+ end
78
+
79
+ def add_dependency(target,depends)
80
+ add_target(target, [depends], []) unless @targets.has_key? target
81
+ @targets[target].add_dependency(depends)
82
+ end
83
+
84
+ def make_dist(project,version)
85
+ distdir = project + '-' + version.to_s
86
+ # XXX-FIXME this should come from the Project
87
+ distfile = project + '-' + version.to_s + '.tar.gz'
88
+ tg = Target.new(distfile)
89
+ tg.add_rule(Platform.rmdir(distdir))
90
+ tg.add_rule("mkdir " + distdir)
91
+ tg.add_rule('$(MAKE) distdir distdir=' + distdir)
92
+ if Platform.is_windows?
93
+
94
+ # FIXME - Not implemented yet
95
+
96
+ else
97
+ tg.add_rule("rm -rf #{distdir}.tar #{distdir}.tar.gz")
98
+ tg.add_rule("tar cf #{distdir}.tar #{distdir}")
99
+ tg.add_rule("gzip #{distdir}.tar")
100
+ tg.add_rule("rm -rf #{distdir}")
101
+ clean("#{distdir}.tar.gz")
102
+ end
103
+ @targets[distfile] = tg
104
+ end
105
+
106
+ def to_s
107
+ res = ''
108
+ @vars.sort.each { |x,y| res += [x, y[0], y[1]].join(' ') + "\n" }
109
+ res += "\n\n"
110
+ res += "default: all\n"
111
+ targets.each { |x,y| throw "#{x} is broken" unless y.is_a? Target }
112
+ @targets.sort.each { |x,y| res += y.to_s }
113
+ res
114
+ end
115
+
116
+ def write(path)
117
+ f = File.open(path, 'w')
118
+ f.print "# AUTOMATICALLY GENERATED -- DO NOT EDIT\n"
119
+ f.print self.to_s
120
+ f.close
121
+ end
122
+
123
+ protected
124
+
125
+ attr_reader :vars, :targets
126
+ end
@@ -0,0 +1,90 @@
1
+ # A packager produces a package in the preferred OS format (e.g. RPM, DEB)
2
+ class Packager
3
+
4
+ attr_reader :makefile
5
+
6
+ def initialize(project)
7
+ @project = project
8
+ @makefile = Makefile.new
9
+ end
10
+
11
+ def finalize
12
+ make_rpm_spec
13
+ @makefile.add_target(
14
+ 'package',
15
+ ['clean', @project.distfile],
16
+ ['rm -rf rpm *.rpm',
17
+ 'mkdir -p rpm/BUILD rpm/RPMS rpm/SOURCES rpm/SPECS rpm/SRPMS',
18
+ 'mkdir -p rpm/RPMS/i386 rpm/RPMS/x86_64',
19
+ "cp #{@project.distfile} rpm/SOURCES",
20
+ 'rpmbuild --define "_topdir ./rpm" -bs rpm.spec',
21
+ 'mv ./rpm/SRPMS/* .',
22
+ 'rm -rf rpm',
23
+ ])
24
+ @makefile.add_rule('clean', Platform.rm('*.rpm')) # FIXME: wildcard is bad
25
+ @makefile.add_rule('distclean', Platform.rm('rpm.spec'))
26
+ end
27
+
28
+ # Generate an RPM spec file
29
+ def make_rpm_spec
30
+ puts 'creating rpm.spec'
31
+ f = File.open('rpm.spec', 'w')
32
+ f.puts <<EOF
33
+ # DO NOT EDIT -- automatically generated by ./configure
34
+ Name: #{@project.id}
35
+ Summary: #{@project.summary}
36
+ Version: #{@project.version}
37
+ Release: 1
38
+ License: #{@project.license}
39
+ Vendor: #{@project.author}
40
+ Group: System Environment/Libraries
41
+ Source0: %{name}-%version.tar.gz
42
+
43
+ %description
44
+ #{@project.description}
45
+
46
+ %package devel
47
+ Summary: Header files, libraries and development documentation for %{name}
48
+ Group: Development/Libraries
49
+ Requires: %{name} = %{version}-%{release}
50
+
51
+ %description devel
52
+ This package contains the header files, static libraries and development
53
+ documentation for %{name}. If you like to develop programs using %{name},
54
+ you will need to install %{name}-devel.
55
+
56
+ %prep
57
+ %setup -q -n PROGRAM-VERSION
58
+
59
+ %build
60
+ ./configure --prefix=/usr
61
+ make
62
+
63
+ %install
64
+ make DESTDIR=%{buildroot} install
65
+
66
+ %clean
67
+ [ %{buildroot} != "/" ] && rm -rf %{buildroot}
68
+
69
+ %post -p /sbin/ldconfig
70
+ %postun -p /sbin/ldconfig
71
+
72
+ %files
73
+ %defattr(-,root,root)
74
+
75
+ /usr/lib/*.so.*
76
+
77
+ %files devel
78
+ %defattr(-,root,root)
79
+
80
+ /usr/lib/*.so
81
+ /usr/include/*
82
+ /usr/share/man/man3/*
83
+
84
+ %changelog
85
+ * Thu Jan 01 2011 Some Person <nobody@nobody.invalid> - #{@project.version}-1
86
+ - automatically generated spec file
87
+ EOF
88
+ f.close()
89
+ end
90
+ end
@@ -0,0 +1,213 @@
1
+ #
2
+ # Abstraction for platform-specific system commands and variables
3
+ # This class only contains static methods.
4
+ #
5
+ class Platform
6
+
7
+ attr_reader :host_os, :target_os
8
+
9
+ require 'rbconfig'
10
+
11
+ # TODO: allow target_os to be overridden for cross-compiling
12
+
13
+ @@host_os = RbConfig::CONFIG['host_os']
14
+ @@target_os = @@host_os
15
+
16
+ # Returns true or false depending on if the target is MS Windows
17
+ def Platform.is_windows?
18
+ @@target_os =~ /mswin|mingw/
19
+ end
20
+
21
+ # Returns true or false depending on if the target is Solaris
22
+ def Platform.is_solaris?
23
+ @@target_os =~ /^solaris/
24
+ end
25
+
26
+ # Returns true or false depending on if the target is Linux
27
+ def Platform.is_linux?
28
+ @@target_os =~ /^linux/
29
+ end
30
+
31
+ # Returns true or false depending on if the target is x86-compatible
32
+ def Platform.is_x86?
33
+ RbConfig::CONFIG['host_cpu'] =~ /^(x86_64|i386)$/ ? true : false
34
+ end
35
+
36
+ # Returns the name of the operating system vendor
37
+ def Platform.vendor
38
+ return 'Fedora' if File.exists?('/etc/fedora-release')
39
+ return 'Red Hat' if File.exists?('/etc/redhat-release')
40
+ return 'Debian' if File.exists?('/etc/debian_version')
41
+ return 'Unknown'
42
+ end
43
+
44
+ # Returns the native word size
45
+ def Platform.word_size
46
+ if @@host_os =~ /^solaris/
47
+ `/usr/bin/isainfo -b`.to_i
48
+ elsif @@host_os =~ /^linux/
49
+ if `/usr/bin/file /bin/bash` =~ /32-bit/
50
+ return 32
51
+ else
52
+ return 64
53
+ end
54
+ else
55
+ throw 'Unknown word size'
56
+ end
57
+ end
58
+
59
+ def Platform.archiver(archive,members)
60
+ if is_windows? && ! ENV['MSYSTEM']
61
+ 'lib.exe ' + members.join(' ') + ' /OUT:' + archive
62
+ else
63
+ # TODO: add '/usr/bin/strip --strip-unneeded' + archive
64
+ 'ar rs ' + archive + ' ' + members.join(' ')
65
+ end
66
+ end
67
+
68
+ # Create a directory and all of it's components
69
+ def Platform.mkdir(path)
70
+ if path.kind_of?(Array)
71
+ path = path.flatten.join(' ')
72
+ end
73
+ throw 'invalid path' if path.nil? or path == ''
74
+ if is_windows?
75
+ "mkdir '#{path}'"
76
+ else
77
+ "umask 22 ; mkdir -p '#{path}'"
78
+ end
79
+ end
80
+
81
+ # Remove a regular file
82
+ def Platform.rm(path)
83
+ if path.kind_of?(Array)
84
+ path = path.join(' ')
85
+ end
86
+ if is_windows? && ! ENV['MSYSTEM']
87
+ return 'del /F ' + path
88
+ else
89
+ return 'rm -f ' + path
90
+ end
91
+ end
92
+
93
+ # Remove a directory along with all of it's contents
94
+ def Platform.rmdir(path)
95
+ is_windows? ? "rmdir /S /Q #{path}" : "rm -rf #{path}"
96
+ end
97
+
98
+ def Platform.cp(src,dst)
99
+ if src.kind_of?(Array)
100
+ src = src.join(' ')
101
+ end
102
+
103
+ if is_windows? && ! ENV['MSYSTEM']
104
+ return "copy #{src} #{dst}"
105
+ else
106
+ return "cp #{src} #{dst}"
107
+ end
108
+ end
109
+
110
+ # Send all output to /dev/null or it's equivalent
111
+ def Platform.dev_null
112
+ if is_windows? && ! ENV['MSYSTEM']
113
+ ' >NUL 2>NUL'
114
+ else
115
+ ' >/dev/null 2>&1'
116
+ end
117
+ end
118
+
119
+ # Send standard error to /dev/null or it's equivalent
120
+ def Platform.dev_null_stderr
121
+ if is_windows? && ! ENV['MSYSTEM']
122
+ ' 2>NUL'
123
+ else
124
+ ' 2>/dev/null'
125
+ end
126
+ end
127
+
128
+ # The extension used for executable files
129
+ def Platform.executable_extension
130
+ is_windows? ? '.exe' : ''
131
+ end
132
+
133
+ # The extension used for intermediate object files
134
+ def Platform.object_extension
135
+ is_windows? ? '.obj' : '.o'
136
+ end
137
+
138
+ # The extension used for static libraries
139
+ def Platform.static_library_extension
140
+ is_windows? ? '.lib' : '.a'
141
+ end
142
+
143
+ # The extension used for shared libraries
144
+ def Platform.shared_library_extension
145
+ is_windows? ? '.dll' : '.so'
146
+ end
147
+
148
+ # Returns true if the current environment supports graphical display
149
+ def Platform.is_graphical?
150
+ return true if Platform.is_windows?
151
+ return true if ENV.has_key?('DISPLAY') and not ENV['DISPLAY'].empty?
152
+ return false
153
+ end
154
+
155
+ # Emulate the which(1) command
156
+ def Platform.which(command)
157
+ return nil if is_windows? # FIXME: STUB
158
+ ENV['PATH'].split(':').each do |prefix|
159
+ path = prefix + '/' + command
160
+ return command if File.executable?(path)
161
+ end
162
+ nil
163
+ end
164
+
165
+ # Converts a slash-delimited path into a backslash-delimited path on Windows.
166
+ def Platform.pathspec(path)
167
+ if is_windows?
168
+ path.gsub(%r{/}) { "\\" }
169
+ else
170
+ path
171
+ end
172
+ end
173
+
174
+ # Run an external command. On Windows, the system() function uses
175
+ # cmd.exe which pops up an ugly DOS window.
176
+ def Platform.execute(cmd)
177
+ if is_windows?
178
+ # NEED INFO
179
+ #shell = WIN32OLE.new('Shell.Application')
180
+ #shell.ShellExecute('cmd.exe', '', ,,
181
+
182
+ # DOESNOT WORK
183
+ #p = IO.popen(cmd)
184
+ # p.readlines
185
+ # return $?.exitstatus == 0 ? true : false
186
+
187
+
188
+
189
+ # NOTE: requires Ruby 1.9
190
+ # pid = Process.spawn(cmd)
191
+ # pid, status = Process.waitpid2(pid)
192
+ # return status.exitstatus == 0 ? true : false
193
+
194
+ return system(cmd)
195
+ else
196
+ system(cmd)
197
+ end
198
+ end
199
+
200
+ # Returns true if the user is running as the superuser on Unix
201
+ # or has Administrator privileges on Windows.
202
+ def Platform.is_superuser?
203
+ if is_windows?
204
+ Process.euid == 0
205
+ else
206
+ system "reg query HKU\\S-1-5-19" + Platform.dev_null
207
+ end
208
+ end
209
+
210
+ if is_windows?
211
+ require 'win32ole'
212
+ end
213
+ end
@@ -0,0 +1,19 @@
1
+ # A project contains all of the information about the build.
2
+ #
3
+ class Project
4
+
5
+ require 'makeconf/systemtype'
6
+ require 'makeconf/baseproject'
7
+ require 'makeconf/androidproject'
8
+
9
+ def self.new(options)
10
+ if SystemType.host =~ /-androideabi$/
11
+ object = AndroidProject.allocate
12
+ else
13
+ object = BaseProject.allocate
14
+ end
15
+ object.send :initialize, options
16
+ object
17
+ end
18
+
19
+ end
@@ -0,0 +1,35 @@
1
+ # Detect the build system type and allow for cross-compilation
2
+ #
3
+ class SystemType
4
+
5
+ # FIXME: detect 'build' properly
6
+ @@build = RbConfig::CONFIG['host_os']
7
+ @@host = nil
8
+ @@target = nil
9
+
10
+ ARGV.each do |arg|
11
+ case arg
12
+ when /^--build=(.*)$/
13
+ @@build = $1
14
+ puts "determining build system type.. #{@@build}"
15
+ when /^--host=(.*)$/
16
+ @@host = $1
17
+ puts "determining host system type.. #{@@host}"
18
+ when /^--target=(.*)$/
19
+ @@target = $1
20
+ end
21
+ end
22
+
23
+ def SystemType.build
24
+ @@build
25
+ end
26
+
27
+ def SystemType.host
28
+ @@host
29
+ end
30
+
31
+ def SystemType.target
32
+ @@target
33
+ end
34
+
35
+ end