fcshd 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +14 -0
- data/bin/fcshc +116 -0
- data/bin/fcshd +24 -0
- data/fcshd.gemspec +19 -0
- data/lib/fcshd.rb +8 -0
- data/lib/fcshd/compiler-output-writer.rb +19 -0
- data/lib/fcshd/compiler-output.rb +91 -0
- data/lib/fcshd/compiler.rb +145 -0
- data/lib/fcshd/logger.rb +41 -0
- data/lib/fcshd/problem.rb +153 -0
- data/lib/fcshd/server.rb +34 -0
- data/lib/fcshd/source-location.rb +27 -0
- data/lib/fcshd/version.rb +5 -0
- metadata +85 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2@fcshd --create
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/bin/fcshc
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "fcshd"
|
5
|
+
rescue LoadError
|
6
|
+
require "pathname"
|
7
|
+
$: << File.expand_path("../../lib", Pathname.new(__FILE__).realpath)
|
8
|
+
require "fcshd"
|
9
|
+
end
|
10
|
+
|
11
|
+
require "optparse"
|
12
|
+
require "socket"
|
13
|
+
|
14
|
+
def die(message)
|
15
|
+
puts "#{File.basename($0)}: #{message}"
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
19
|
+
$source_directories = []
|
20
|
+
$library_filenames = []
|
21
|
+
$extra_arguments = []
|
22
|
+
|
23
|
+
OptionParser.new do |parser|
|
24
|
+
parser.banner = "Usage: fcshc MAIN [SRCDIR|LIB]... -o OUTPUT"
|
25
|
+
|
26
|
+
parser.on("--halo", "Use the Halo theme") do
|
27
|
+
if ENV["FLEX_HOME"]
|
28
|
+
$extra_arguments << "-theme=" +
|
29
|
+
File.join(ENV["FLEX_HOME"], *%w(frameworks themes Halo halo.swc))
|
30
|
+
else
|
31
|
+
die "must set $FLEX_HOME to use --halo option"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
parser.on("-o", "--output FILENAME", "Write SWF to FILENAME") do |value|
|
36
|
+
$output_filename = File.expand_path(value)
|
37
|
+
end
|
38
|
+
|
39
|
+
parser.on("-3", "Use -compatibility-version=3") do |value|
|
40
|
+
$extra_arguments << "-compatibility-version=3"
|
41
|
+
end
|
42
|
+
|
43
|
+
parser.on("-X EXTRA-ARGUMENT", "Pass EXTRA-ARGUMENT to mxmlc") do |value|
|
44
|
+
$extra_arguments << value
|
45
|
+
end
|
46
|
+
|
47
|
+
parser.on("-v", "--verbose", "Print mxmlc command line") do
|
48
|
+
$verbose = true
|
49
|
+
end
|
50
|
+
end.parse!
|
51
|
+
|
52
|
+
for name in ARGV
|
53
|
+
if not File.exists? name
|
54
|
+
die "no such file or directory: #{name}"
|
55
|
+
elsif File.directory? name
|
56
|
+
$source_directories << File.expand_path(name)
|
57
|
+
elsif %w(.as .mxml).include? File.extname(name)
|
58
|
+
die "multiple source files not allowed" if $source_filename
|
59
|
+
$source_filename = File.expand_path(name)
|
60
|
+
elsif %w(.swc).include? File.extname(name)
|
61
|
+
$library_filenames << File.expand_path(name)
|
62
|
+
else
|
63
|
+
die "don't know what to do with file: #{name}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if $source_filename == nil
|
68
|
+
die "missing source file to compile"
|
69
|
+
end
|
70
|
+
|
71
|
+
if $output_filename == nil
|
72
|
+
$source_filename.sub(/\.(as|mxml)$/, ".swf").tap do |filename|
|
73
|
+
local_filename = File.basename(filename)
|
74
|
+
$output_filename = File.expand_path(local_filename)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
$fcshd_arguments = ["mxmlc #$source_filename -output=#$output_filename"]
|
79
|
+
|
80
|
+
for directory in $source_directories
|
81
|
+
$fcshd_arguments << "-compiler.source-path+=#{directory}"
|
82
|
+
end
|
83
|
+
|
84
|
+
for filename in $library_filenames
|
85
|
+
$fcshd_arguments << "-compiler.library-path+=#{filename}"
|
86
|
+
end
|
87
|
+
|
88
|
+
$fcshd_arguments.concat($extra_arguments)
|
89
|
+
|
90
|
+
begin
|
91
|
+
host, port = "localhost", 34345
|
92
|
+
socket = TCPSocket.new(host, port)
|
93
|
+
rescue Errno::ECONNREFUSED
|
94
|
+
die "Could not connect to fcshd at #{host}:#{port}."
|
95
|
+
end
|
96
|
+
|
97
|
+
basedir = File.join(File.expand_path("."), "")
|
98
|
+
|
99
|
+
fcshd_command = $fcshd_arguments.join(" ")
|
100
|
+
warn fcshd_command.gsub(basedir, "") if $verbose
|
101
|
+
socket.puts fcshd_command
|
102
|
+
|
103
|
+
compiler_output = ""
|
104
|
+
socket.each_line do |line|
|
105
|
+
case line
|
106
|
+
when /^fcshd: /
|
107
|
+
warn line.chomp
|
108
|
+
else
|
109
|
+
compiler_output << line
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
output = FCSHD::CompilerOutput[compiler_output, basedir]
|
114
|
+
output.write! FCSHD::CompilerOutputWriter.new(STDOUT, basedir)
|
115
|
+
|
116
|
+
exit 1 if not output.succeeded?
|
data/bin/fcshd
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "fcshd"
|
5
|
+
rescue LoadError
|
6
|
+
require "pathname"
|
7
|
+
$: << File.expand_path("../../lib", Pathname.new(__FILE__).realpath)
|
8
|
+
require "fcshd"
|
9
|
+
end
|
10
|
+
|
11
|
+
logger = FCSHD::Logger.new(STDERR)
|
12
|
+
compiler = FCSHD::Compiler.new(logger)
|
13
|
+
compiler.start!
|
14
|
+
|
15
|
+
Thread.abort_on_exception = true
|
16
|
+
|
17
|
+
FCSHD_PORT = 34345
|
18
|
+
|
19
|
+
begin
|
20
|
+
logger.log "Listening to port #{FCSHD_PORT}."
|
21
|
+
FCSHD::Server.new(FCSHD_PORT, compiler, logger).run!
|
22
|
+
rescue Interrupt
|
23
|
+
logger.log "Exiting."
|
24
|
+
end
|
data/fcshd.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "./lib/fcshd/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "fcshd"
|
5
|
+
gem.summary = "Usable CLI for the Adobe Flex compiler shell (fcsh)"
|
6
|
+
gem.description = <<'^D'
|
7
|
+
By using a client-server architecture, we are able to make the Adobe Flex
|
8
|
+
compiler run fast while still being usable in command-line environments.
|
9
|
+
In practice, you start the `fcshd' server, and are then able to use the
|
10
|
+
client program `fcshc' as a faster and more usable replacement for `mxmlc'.
|
11
|
+
^D
|
12
|
+
gem.version = FCSHD::VERSION
|
13
|
+
gem.author = "Daniel Brockman"
|
14
|
+
gem.email = "daniel@gointeractive.se"
|
15
|
+
gem.date = Time.now.utc.strftime("%Y-%m-%d")
|
16
|
+
gem.homepage = "http://github.com/dbrock/fcshd"
|
17
|
+
gem.files = `git ls-files`.lines.map(&:chomp)
|
18
|
+
gem.executables = %w"fcshd fcshc"
|
19
|
+
end
|
data/lib/fcshd.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module FCSHD
|
2
|
+
class CompilerOutputWriter
|
3
|
+
def initialize(output, basedir)
|
4
|
+
@output = output
|
5
|
+
@basedir = basedir
|
6
|
+
end
|
7
|
+
|
8
|
+
def write_uninterpreted_line! line
|
9
|
+
@output.puts line
|
10
|
+
end
|
11
|
+
|
12
|
+
def write_problem! problem
|
13
|
+
location = problem.source_location.with_basedir(@basedir)
|
14
|
+
for line in problem.formatted_message_lines do
|
15
|
+
@output.puts "#{location}: #{line}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module FCSHD
|
2
|
+
class CompilerOutput
|
3
|
+
def self.[] text, basedir
|
4
|
+
new.tap do |result|
|
5
|
+
Parser.new(text.lines.entries, basedir, result).parse!
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :n_compiled_files
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@events = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def << event
|
16
|
+
@events << event
|
17
|
+
end
|
18
|
+
|
19
|
+
def write! writer
|
20
|
+
for event in @events
|
21
|
+
case event
|
22
|
+
when String
|
23
|
+
writer.write_uninterpreted_line! event
|
24
|
+
when Problem
|
25
|
+
writer.write_problem! event
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def nothing_to_do?
|
31
|
+
n_compiled_files == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def succeeded!
|
35
|
+
@succeeded = true
|
36
|
+
end
|
37
|
+
|
38
|
+
def succeeded?
|
39
|
+
@succeeded
|
40
|
+
end
|
41
|
+
|
42
|
+
class Parser < Struct.new(:lines, :basedir, :result)
|
43
|
+
def parse!
|
44
|
+
parse_line! until lines.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_line!
|
48
|
+
take_line! do |line|
|
49
|
+
case line
|
50
|
+
when /^(Recompile|Reason): /
|
51
|
+
when /^Loading configuration file /
|
52
|
+
when /^(.+\.swf) \((\d+) bytes\)$/
|
53
|
+
result.succeeded!
|
54
|
+
when "Nothing has changed since the last compile. Skip..."
|
55
|
+
result.n_compiled_files = 0
|
56
|
+
when /^Files changed: (\d+) Files affected: (\d+)$/
|
57
|
+
result.n_compiled_files = $1.to_i + $2.to_i
|
58
|
+
when /^(\/.+?)(?:\((\d+)\))?: (?:col: (\d+) )?(.+)$/
|
59
|
+
location = SourceLocation[$1, $2.to_i, $3.to_i, basedir]
|
60
|
+
result << Problem[location, $4]
|
61
|
+
skip_indented_lines!
|
62
|
+
when /^Required RSLs:$/
|
63
|
+
skip_indented_lines!
|
64
|
+
when /^fcshd: /
|
65
|
+
result << line
|
66
|
+
else
|
67
|
+
result << "mxmlc: #{line}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def take_line!
|
73
|
+
yield skip_line!
|
74
|
+
end
|
75
|
+
|
76
|
+
def skip_line!
|
77
|
+
current_line
|
78
|
+
ensure
|
79
|
+
lines.shift
|
80
|
+
end
|
81
|
+
|
82
|
+
def current_line
|
83
|
+
lines[0].chomp
|
84
|
+
end
|
85
|
+
|
86
|
+
def skip_indented_lines!
|
87
|
+
skip_line! until lines.empty? or current_line =~ /^\S/
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require "find"
|
2
|
+
|
3
|
+
module FCSHD
|
4
|
+
class Compiler
|
5
|
+
FLEX_HOME = ENV["FLEX_HOME"]
|
6
|
+
|
7
|
+
def self.flex_path(*components)
|
8
|
+
File.join(FLEX_HOME, *components)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.standard_fcsh_executable
|
12
|
+
FLEX_HOME ? flex_path("bin", "fcsh") : "fcsh"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.standard_source_directory_root
|
16
|
+
flex_path("frameworks", "projects")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.barename(filename)
|
20
|
+
File.basename(filename).sub(/\..*/, "")
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.find_standard_component(name)
|
24
|
+
if FLEX_HOME
|
25
|
+
Find.find(standard_source_directory_root) do |filename|
|
26
|
+
if barename(filename) == name
|
27
|
+
break File.dirname(filename).
|
28
|
+
sub(%r{.+/src/}, "").gsub("/", ".")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
FCSH_EXECUTABLE = ENV["FCSH"] || standard_fcsh_executable
|
35
|
+
|
36
|
+
def initialize(logger)
|
37
|
+
@logger = logger
|
38
|
+
@output_buffer = ""
|
39
|
+
end
|
40
|
+
|
41
|
+
def start!
|
42
|
+
start_fcsh_process!
|
43
|
+
parse_fcsh_boilerplate!
|
44
|
+
@logger.log "Started Flex #@flex_version compiler shell."
|
45
|
+
rescue PromptNotFound => error
|
46
|
+
@logger.error "Could not find fcsh prompt:"
|
47
|
+
@logger.error @logger.format_command(FCSH_EXECUTABLE, error.message)
|
48
|
+
@logger.log "Please set $FLEX_HOME or $FCSH." if
|
49
|
+
error.message.include? "command not found"
|
50
|
+
@logger.exit
|
51
|
+
end
|
52
|
+
|
53
|
+
def compile! command, frontend
|
54
|
+
@command, @frontend = command, frontend
|
55
|
+
|
56
|
+
if have_command_id?
|
57
|
+
recompile!
|
58
|
+
else
|
59
|
+
compile_new!
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def start_fcsh_process!
|
66
|
+
stop_fcsh_process!
|
67
|
+
@fcsh_process = IO.popen("#{FCSH_EXECUTABLE} 2>&1", "r+")
|
68
|
+
read_fcsh_output!
|
69
|
+
end
|
70
|
+
|
71
|
+
def stop_fcsh_process!
|
72
|
+
@fcsh_process.close if @fcsh_process
|
73
|
+
@fcsh_process = nil
|
74
|
+
@command_ids = {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_fcsh_boilerplate!
|
78
|
+
case @output
|
79
|
+
when /Version (\S+)/
|
80
|
+
@flex_version = $1
|
81
|
+
else
|
82
|
+
@flex_version = "(unknown version)"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def compile_new!
|
87
|
+
@frontend.puts "fcshd: Compiling from scratch..."
|
88
|
+
@frontend.flush
|
89
|
+
send_fcsh_command! @command
|
90
|
+
parse_compilation_output!
|
91
|
+
ensure
|
92
|
+
if not have_command_id?
|
93
|
+
@logger.error "Could not determine compilation ID:"
|
94
|
+
@logger.error @logger.format_command("(fcsh) #@command", @output)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def recompile!
|
99
|
+
send_fcsh_command! "compile #{command_id}"
|
100
|
+
parse_compilation_output!
|
101
|
+
end
|
102
|
+
|
103
|
+
def command_id
|
104
|
+
@command_ids[@command]
|
105
|
+
end
|
106
|
+
|
107
|
+
def command_id= id
|
108
|
+
@command_ids[@command] = id
|
109
|
+
end
|
110
|
+
|
111
|
+
def have_command_id?
|
112
|
+
@command_ids.include? @command
|
113
|
+
end
|
114
|
+
|
115
|
+
def parse_compilation_output!
|
116
|
+
for line in @output.lines
|
117
|
+
case line
|
118
|
+
when /^fcsh: Assigned (\d+) as the compile target id$/
|
119
|
+
self.command_id = $1
|
120
|
+
else
|
121
|
+
@frontend.puts(line)
|
122
|
+
@logger.log_raw(line) if line =~ /^fcsh: /
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def send_fcsh_command! command
|
128
|
+
@logger.log_raw("> #{command}")
|
129
|
+
@fcsh_process.puts(command)
|
130
|
+
read_fcsh_output!
|
131
|
+
end
|
132
|
+
|
133
|
+
FCSH_PROMPT = "\n(fcsh) "
|
134
|
+
class PromptNotFound < Exception ; end
|
135
|
+
|
136
|
+
def read_fcsh_output!
|
137
|
+
@output_buffer << @fcsh_process.readpartial(256) until
|
138
|
+
@output_buffer.include? FCSH_PROMPT
|
139
|
+
@output, @output_buffer =
|
140
|
+
@output_buffer.split(FCSH_PROMPT, 2)
|
141
|
+
rescue EOFError
|
142
|
+
raise PromptNotFound, @output_buffer
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/lib/fcshd/logger.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module FCSHD
|
2
|
+
class Logger
|
3
|
+
def initialize(output)
|
4
|
+
@stderr = output
|
5
|
+
end
|
6
|
+
|
7
|
+
def log_raw(message)
|
8
|
+
@stderr.puts message
|
9
|
+
end
|
10
|
+
|
11
|
+
def log(message)
|
12
|
+
for line in message.lines
|
13
|
+
log_raw "#{program_name}: #{line.chomp}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def program_name
|
18
|
+
File.basename($0)
|
19
|
+
end
|
20
|
+
|
21
|
+
def error(message)
|
22
|
+
for line in message.lines
|
23
|
+
log "error: #{line.chomp}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def die(message)
|
28
|
+
error message ; exit
|
29
|
+
end
|
30
|
+
|
31
|
+
def exit(code=1)
|
32
|
+
Kernel.exit code
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_command(command, output)
|
36
|
+
["$ #{command}", *output.lines].
|
37
|
+
map { |line| " #{line.chomp}" }.
|
38
|
+
join("\n")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module FCSHD
|
4
|
+
class Problem < Struct.new(:source_location, :raw_mxmlc_message)
|
5
|
+
ERROR_PREFIX = /^\s*Error: /
|
6
|
+
|
7
|
+
def mxmlc_message
|
8
|
+
raw_mxmlc_message.sub(ERROR_PREFIX, "")
|
9
|
+
end
|
10
|
+
|
11
|
+
def error?
|
12
|
+
raw_mxmlc_message =~ ERROR_PREFIX
|
13
|
+
end
|
14
|
+
|
15
|
+
def quote(string)
|
16
|
+
"‘#{string}’"
|
17
|
+
end
|
18
|
+
|
19
|
+
def message
|
20
|
+
case raw = raw_message
|
21
|
+
when String then raw
|
22
|
+
when Array then raw * "\n"
|
23
|
+
else fail
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def raw_message
|
28
|
+
case mxmlc_message
|
29
|
+
|
30
|
+
when "Unable to resolve MXML language version. Please specify the language namespace on the root document tag."
|
31
|
+
then
|
32
|
+
<<"^D"
|
33
|
+
missing MXML version
|
34
|
+
→ xmlns:fx="library://ns.adobe.com/mxml/2009"
|
35
|
+
→ xmlns="library://ns.adobe.com/flex/spark"
|
36
|
+
→ xmlns:mx="library://ns.adobe.com/flex/mx" (Flex 3 compatibility)
|
37
|
+
→ xmlns="http://www.adobe.com/2006/mxml" (Flex 3)
|
38
|
+
^D
|
39
|
+
|
40
|
+
when /^Incorrect number of arguments. Expected (\d+)\.$/
|
41
|
+
then "expected #$1 arguments"
|
42
|
+
|
43
|
+
when /^(?:Access of possibly undefined property|Call to a possibly undefined method) (.+) through a reference with static type (.+)\.$/
|
44
|
+
then "#{quote $1} undeclared in #$2"
|
45
|
+
|
46
|
+
when /^Attempted access of inaccessible property (.+) through a reference with static type (.+)\.$/
|
47
|
+
then "#{quote $1} inaccessible in #$2"
|
48
|
+
|
49
|
+
when
|
50
|
+
/^Could not resolve <(.+)> to a component implementation.$/,
|
51
|
+
/^Call to a possibly undefined method (.+).$/,
|
52
|
+
/^Access of undefined property (.+).$/,
|
53
|
+
/^The definition of base class (.+) was not found.$/,
|
54
|
+
/^Type was not found or was not a compile-time constant: (.+)\.$/
|
55
|
+
then
|
56
|
+
["#{quote $1} undeclared"].tap do |result|
|
57
|
+
Compiler.find_standard_component($1).tap do |package|
|
58
|
+
result << "→ import #{package}.*" if package
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
when /^Definition (.+) could not be found\.$/
|
63
|
+
then "#{quote $1} not found"
|
64
|
+
|
65
|
+
when
|
66
|
+
/^Implicit coercion of a value of type (.+) to an unrelated type (.+)\.$/,
|
67
|
+
|
68
|
+
/^Implicit coercion of a value with static type (.+) to a possibly unrelated type (.+)\./
|
69
|
+
|
70
|
+
then
|
71
|
+
actual, expected = $1, $2
|
72
|
+
expected_base = expected.sub(/.+:/, "")
|
73
|
+
actual_base = actual.sub(/.+:/, "")
|
74
|
+
if actual_base != expected_base
|
75
|
+
"expected #{expected_base} (got #{actual_base})"
|
76
|
+
else
|
77
|
+
"expected #{expected} (got #{actual})"
|
78
|
+
end
|
79
|
+
|
80
|
+
when "Method marked override must override another method."
|
81
|
+
then "overriding nonexistent method"
|
82
|
+
|
83
|
+
when "Overriding a function that is not marked for override."
|
84
|
+
then ["unmarked override", "→ add override keyword"]
|
85
|
+
|
86
|
+
when "Incompatible override."
|
87
|
+
then "incompatible override"
|
88
|
+
|
89
|
+
when /^Ambiguous reference to (.+)\.$/
|
90
|
+
then "#{quote $1} is ambiguous"
|
91
|
+
|
92
|
+
when /^A conflict exists with definition (.+) in namespace internal\.$/
|
93
|
+
then "#{quote $1} is conflicting"
|
94
|
+
|
95
|
+
when
|
96
|
+
/^Warning: parameter '(.+)' has no type declaration\.$/,
|
97
|
+
/^Warning: return value for function '(.+)' has no type declaration\.$/
|
98
|
+
then
|
99
|
+
case $1
|
100
|
+
when "anonymous" then "anonymous function"
|
101
|
+
else quote $1
|
102
|
+
end + " missing type declaration"
|
103
|
+
|
104
|
+
when /^A file found in a source-path must have the same package structure '(.*)', as the definition's package, '(.*)'\.$/
|
105
|
+
then "package should be #{quote $1}"
|
106
|
+
|
107
|
+
when /^Comparison between a value with static type (.+) and a possibly unrelated type (.+)\.$/
|
108
|
+
then "comparing #$1 to #$2"
|
109
|
+
|
110
|
+
when "Illegal assignment to a variable specified as constant."
|
111
|
+
then "modifying constant"
|
112
|
+
|
113
|
+
when "Function does not have a body."
|
114
|
+
then "missing function body"
|
115
|
+
|
116
|
+
when "Return type of a setter definition must be unspecified or void."
|
117
|
+
then "setter must return void"
|
118
|
+
|
119
|
+
when "Function does not return a value."
|
120
|
+
then "missing return statement"
|
121
|
+
|
122
|
+
when "Syntax error: expecting identifier before rightparen."
|
123
|
+
then "#{quote ")"} unexpected"
|
124
|
+
|
125
|
+
when
|
126
|
+
/^The (.+) attribute can only be used inside a package\./,
|
127
|
+
/^The (.+) attribute may be used only on class property definitions\./
|
128
|
+
then
|
129
|
+
"#{quote $1} unexpected"
|
130
|
+
|
131
|
+
else
|
132
|
+
mxmlc_message
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def formatted_message_lines
|
137
|
+
lines = message.lines.entries
|
138
|
+
if error?
|
139
|
+
first = <<"^D"
|
140
|
+
error: #{lines[0].chomp}
|
141
|
+
^D
|
142
|
+
rest = lines[1..-1].map do |line|
|
143
|
+
<<"^D"
|
144
|
+
#{line.chomp}
|
145
|
+
^D
|
146
|
+
end
|
147
|
+
[first] + rest
|
148
|
+
else
|
149
|
+
lines
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/lib/fcshd/server.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "socket"
|
2
|
+
|
3
|
+
module FCSHD
|
4
|
+
class Server
|
5
|
+
def initialize(port, compiler, logger)
|
6
|
+
@server = TCPServer.new("localhost", port)
|
7
|
+
@compiler = compiler
|
8
|
+
@logger = logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def run!
|
12
|
+
loop do
|
13
|
+
Thread.start(@server.accept) do |socket|
|
14
|
+
Client.new(socket, @compiler).run!
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Client < Struct.new(:socket, :compiler)
|
20
|
+
def run!
|
21
|
+
case command = socket.gets
|
22
|
+
when /^mxmlc /
|
23
|
+
compiler.compile! command, socket
|
24
|
+
else
|
25
|
+
sockets.puts "fcshd: Unrecognized command: #{command}"
|
26
|
+
end
|
27
|
+
rescue Errno::EPIPE
|
28
|
+
logger.log "Broken pipe."
|
29
|
+
ensure
|
30
|
+
socket.close
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module FCSHD
|
2
|
+
class SourceLocation < Struct.new \
|
3
|
+
:filename, :line_number, :column_number, :basedir
|
4
|
+
|
5
|
+
def to_s
|
6
|
+
relative_filename.tap do |result|
|
7
|
+
result << ":#{line_number}" if line_number
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def relative_filename
|
12
|
+
if basedir
|
13
|
+
filename.sub(/^#{Regexp.quote(basedir)}/, "")
|
14
|
+
else
|
15
|
+
filename
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def with_basedir(new_basedir)
|
20
|
+
dup.tap { |result| result.basedir = new_basedir }
|
21
|
+
end
|
22
|
+
|
23
|
+
def without_column_number
|
24
|
+
dup.tap { |result| result.column_number = nil }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fcshd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Daniel Brockman
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-08-31 00:00:00 Z
|
18
|
+
dependencies: []
|
19
|
+
|
20
|
+
description: |
|
21
|
+
By using a client-server architecture, we are able to make the Adobe Flex
|
22
|
+
compiler run fast while still being usable in command-line environments.
|
23
|
+
In practice, you start the `fcshd' server, and are then able to use the
|
24
|
+
client program `fcshc' as a faster and more usable replacement for `mxmlc'.
|
25
|
+
|
26
|
+
email: daniel@gointeractive.se
|
27
|
+
executables:
|
28
|
+
- fcshd
|
29
|
+
- fcshc
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files: []
|
33
|
+
|
34
|
+
files:
|
35
|
+
- .gitignore
|
36
|
+
- .rvmrc
|
37
|
+
- Gemfile
|
38
|
+
- Gemfile.lock
|
39
|
+
- bin/fcshc
|
40
|
+
- bin/fcshd
|
41
|
+
- fcshd.gemspec
|
42
|
+
- lib/fcshd.rb
|
43
|
+
- lib/fcshd/compiler-output-writer.rb
|
44
|
+
- lib/fcshd/compiler-output.rb
|
45
|
+
- lib/fcshd/compiler.rb
|
46
|
+
- lib/fcshd/logger.rb
|
47
|
+
- lib/fcshd/problem.rb
|
48
|
+
- lib/fcshd/server.rb
|
49
|
+
- lib/fcshd/source-location.rb
|
50
|
+
- lib/fcshd/version.rb
|
51
|
+
homepage: http://github.com/dbrock/fcshd
|
52
|
+
licenses: []
|
53
|
+
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
76
|
+
version: "0"
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.8.8
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: Usable CLI for the Adobe Flex compiler shell (fcsh)
|
84
|
+
test_files: []
|
85
|
+
|