imsg-grep 0.1.2-darwin
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +95 -0
- data/bin/img2png +0 -0
- data/bin/imsg-grep +749 -0
- data/bin/msg-info +133 -0
- data/bin/sql-shell +25 -0
- data/doc/HELP +181 -0
- data/doc/HELP_DATES +72 -0
- data/ext/extconf.rb +97 -0
- data/ext/img2png.swift +325 -0
- data/lib/imsg-grep/VERSION +1 -0
- data/lib/imsg-grep/apple/attr_str.rb +65 -0
- data/lib/imsg-grep/apple/bplist.rb +257 -0
- data/lib/imsg-grep/apple/keyed_archive.rb +105 -0
- data/lib/imsg-grep/dev/print_query.rb +84 -0
- data/lib/imsg-grep/dev/timer.rb +38 -0
- data/lib/imsg-grep/images/imaginator.rb +135 -0
- data/lib/imsg-grep/images/img2png.dylib +0 -0
- data/lib/imsg-grep/images/img2png.rb +84 -0
- data/lib/imsg-grep/messages.rb +314 -0
- data/lib/imsg-grep/utils/date.rb +117 -0
- data/lib/imsg-grep/utils/strop_utils.rb +79 -0
- metadata +161 -0
data/bin/msg-info
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Display detailed information about a specific message by ID from the iMessage database
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
|
|
6
|
+
require_relative '../lib/imsg-grep/dev/print_query'
|
|
7
|
+
require_relative '../lib/imsg-grep/messages'
|
|
8
|
+
|
|
9
|
+
db = Messages.init
|
|
10
|
+
|
|
11
|
+
def print_results(results2)
|
|
12
|
+
puts "\n#{'=' * 80}"
|
|
13
|
+
|
|
14
|
+
cols, *rows = results2
|
|
15
|
+
width = cols.map(&:length).max
|
|
16
|
+
rows.each do |row|
|
|
17
|
+
row.each_with_index do |val, i|
|
|
18
|
+
if cols[i] == "payload_json" && val
|
|
19
|
+
val = IO.popen("jq -C", "r+") { |jq| jq << val; jq.close_write; jq.read.chomp }
|
|
20
|
+
else
|
|
21
|
+
val = val.nil? ? "NULL" : val.to_s
|
|
22
|
+
val = val.gsub("\n", "\n " + ' ' * width)
|
|
23
|
+
end
|
|
24
|
+
puts "#{cols[i].ljust(width, ' ')} : #{val}"
|
|
25
|
+
end
|
|
26
|
+
puts "-" * 80
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def print_message_details(db, msg_id)
|
|
31
|
+
id_cond = msg_id =~ /-/ ? "orig.guid" : "orig.ROWID"
|
|
32
|
+
print_results db.execute2(<<~SQL, msg_id)
|
|
33
|
+
SELECT
|
|
34
|
+
-- Core identifiers
|
|
35
|
+
m.message_id,
|
|
36
|
+
m.guid,
|
|
37
|
+
m.utc_time,
|
|
38
|
+
-- -- Timing
|
|
39
|
+
-- m.utc_time,
|
|
40
|
+
|
|
41
|
+
-- -- Chat context
|
|
42
|
+
-- m.chat_style,
|
|
43
|
+
-- m.chat_name,
|
|
44
|
+
|
|
45
|
+
-- -- Message content
|
|
46
|
+
|
|
47
|
+
m.sender_name,
|
|
48
|
+
m.sender_searchable,
|
|
49
|
+
m.recipient_searchable,
|
|
50
|
+
m.recipient_name,
|
|
51
|
+
m.recipient_names,
|
|
52
|
+
m.recipients_searchable,
|
|
53
|
+
m.member_names,
|
|
54
|
+
m.members_searchable,
|
|
55
|
+
|
|
56
|
+
m.has_attachments,
|
|
57
|
+
m.is_from_me,
|
|
58
|
+
orig.text as text_original,
|
|
59
|
+
m.text,
|
|
60
|
+
unarchive_string(orig.attributedBody) as text_fresh,
|
|
61
|
+
|
|
62
|
+
-- Raw message fields
|
|
63
|
+
orig.subject,
|
|
64
|
+
orig.service,
|
|
65
|
+
orig.account,
|
|
66
|
+
orig.date,
|
|
67
|
+
orig.date_read,
|
|
68
|
+
orig.date_delivered,
|
|
69
|
+
|
|
70
|
+
-- Thread/Reply
|
|
71
|
+
orig.thread_originator_guid,
|
|
72
|
+
orig.thread_originator_part,
|
|
73
|
+
orig.reply_to_guid,
|
|
74
|
+
|
|
75
|
+
-- Metadata
|
|
76
|
+
orig.version,
|
|
77
|
+
orig.sort_id,
|
|
78
|
+
orig.share_status,
|
|
79
|
+
orig.share_direction,
|
|
80
|
+
orig.group_action_type,
|
|
81
|
+
orig.balloon_bundle_id,
|
|
82
|
+
orig.destination_caller_id,
|
|
83
|
+
orig.associated_message_type,
|
|
84
|
+
orig.associated_message_guid,
|
|
85
|
+
|
|
86
|
+
-- Status flags
|
|
87
|
+
orig.is_delivered,
|
|
88
|
+
orig.is_finished,
|
|
89
|
+
orig.is_emote,
|
|
90
|
+
orig.is_delayed,
|
|
91
|
+
orig.is_auto_reply,
|
|
92
|
+
orig.is_prepared,
|
|
93
|
+
orig.is_read,
|
|
94
|
+
orig.is_system_message,
|
|
95
|
+
orig.is_sent,
|
|
96
|
+
orig.is_forward,
|
|
97
|
+
orig.is_service_message,
|
|
98
|
+
orig.is_spam,
|
|
99
|
+
orig.error,
|
|
100
|
+
|
|
101
|
+
-- Big Data
|
|
102
|
+
HEX(orig.attributedBody) as attributedBody_hex,
|
|
103
|
+
HEX(orig.payload_data) as payload_data_hex,
|
|
104
|
+
unarchive_keyed(m.payload_data) as payload_json
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
FROM _imsg.message orig
|
|
108
|
+
LEFT JOIN message_view m ON m.message_id = orig.ROWID
|
|
109
|
+
WHERE #{id_cond} = ?
|
|
110
|
+
SQL
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
id_cond = msg_id =~ /-/ ? "(SELECT ROWID FROM message WHERE guid = ?)" : "?"
|
|
114
|
+
Print.query(<<~SQL, msg_id)
|
|
115
|
+
SELECT a.ROWID, a.filename, a.mime_type, a.uti,
|
|
116
|
+
a.created_date,
|
|
117
|
+
a.hide_attachment,
|
|
118
|
+
a.total_bytes,
|
|
119
|
+
a.transfer_name
|
|
120
|
+
FROM _imsg.attachment a
|
|
121
|
+
JOIN _imsg.message_attachment_join maj ON a.ROWID = maj.attachment_id
|
|
122
|
+
JOIN _imsg.message m ON maj.message_id = m.ROWID
|
|
123
|
+
WHERE m.ROWID IN (#{id_cond})
|
|
124
|
+
SQL
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
if __FILE__ == $0 && ARGV[0]
|
|
130
|
+
ARGV.each do |id|
|
|
131
|
+
print_message_details(db, id)
|
|
132
|
+
end
|
|
133
|
+
end
|
data/bin/sql-shell
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Interactive SQL shell for querying the iMessage database with readline history
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
|
|
6
|
+
require_relative '../lib/imsg-grep/messages'
|
|
7
|
+
require_relative '../lib/imsg-grep/dev/print_query'
|
|
8
|
+
require 'readline'
|
|
9
|
+
require 'fileutils'
|
|
10
|
+
|
|
11
|
+
$db = Messages.init
|
|
12
|
+
|
|
13
|
+
histfile = "#{ENV['HOME']}/.config/imsg-grep/history"
|
|
14
|
+
FileUtils.mkdir_p File.dirname(histfile)
|
|
15
|
+
Readline::HISTORY.push(*File.readlines(histfile).map(&:chomp)) if File.exist?(histfile)
|
|
16
|
+
|
|
17
|
+
while buf = Readline.readline("sql> ", true)
|
|
18
|
+
break if buf.strip =~ /^(exit|quit)$/i
|
|
19
|
+
begin
|
|
20
|
+
Print.query(buf)
|
|
21
|
+
File.write(histfile, "#{buf}\n", mode: 'a')
|
|
22
|
+
rescue => e
|
|
23
|
+
puts "Error: #{e.message}"
|
|
24
|
+
end
|
|
25
|
+
end
|
data/doc/HELP
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Usage: PROG [options ...] [PATTERN ...]
|
|
2
|
+
|
|
3
|
+
Date filtering:
|
|
4
|
+
-d, --since DATE Match after DATE
|
|
5
|
+
-u, --until DATE Match before DATE
|
|
6
|
+
-Z, --utc Parse and print dates as UTC
|
|
7
|
+
|
|
8
|
+
Participant filtering:
|
|
9
|
+
-t, --to CONTACT Match recipients
|
|
10
|
+
-f, --from CONTACT Match sender
|
|
11
|
+
-w, --with CONTACT Match sender or recipients
|
|
12
|
+
-c, --chat [PATTERN] With no argument, match any group chat
|
|
13
|
+
Otherwise PATTERN matches chat name
|
|
14
|
+
|
|
15
|
+
Message filtering:
|
|
16
|
+
-M, --message PATTERN Match message text
|
|
17
|
+
Useful for boolean expressions: -f x --or -M y
|
|
18
|
+
-s, --sent Only match sent
|
|
19
|
+
-r, --received Only match received
|
|
20
|
+
-V, --service SRV Filter by messaging service
|
|
21
|
+
One of: any|imessage|rcs|sms (default: any)
|
|
22
|
+
-n, --max NUM Only print top NUM messages
|
|
23
|
+
-a, --all Allow calling with no conditions
|
|
24
|
+
(will print every message unless -n)
|
|
25
|
+
|
|
26
|
+
Link filtering:
|
|
27
|
+
-L, --links [PATTERN] Find links with URL matching pattern
|
|
28
|
+
With no argument, find all links
|
|
29
|
+
-Y, --youtube Find youtube links
|
|
30
|
+
-S, --soundcloud Find soundcloud links
|
|
31
|
+
-X, --twitter Find twitter links
|
|
32
|
+
|
|
33
|
+
Pattern modifiers:
|
|
34
|
+
-C, --smart-case Case-sensitive only when pattern has uppercase
|
|
35
|
+
letters (default; turn off with -i or -I)
|
|
36
|
+
-i, --ignore-case Case-insensitive matches
|
|
37
|
+
-I, --no-ignore-case Case-sensitive matches
|
|
38
|
+
-q, --literal Literal string match, no regex
|
|
39
|
+
-x, --regexp Regexp match (default; turn off with -q)
|
|
40
|
+
-v, --invert-match Negate next pattern
|
|
41
|
+
-e, --exact Next pattern must match full string
|
|
42
|
+
-A, --and [PATTERN] Must also appear with previous condition
|
|
43
|
+
-O, --or [PATTERN] Either previous or next condition may occur
|
|
44
|
+
Without argument, affect the following condition
|
|
45
|
+
|
|
46
|
+
Format:
|
|
47
|
+
--[no-]color Enable/disable color output (default: yes if tty)
|
|
48
|
+
-W, --no-wrap Don't wrap text
|
|
49
|
+
-l, --one-line "[from -> to] message", newlines stripped
|
|
50
|
+
With --json, disable pretty print
|
|
51
|
+
-U, --urls Print only URLs in place of message text
|
|
52
|
+
Prefer canonical URLs if available
|
|
53
|
+
No change when no link present
|
|
54
|
+
-m, --no-meta Print only text or capture
|
|
55
|
+
No from/to headers, link or file details
|
|
56
|
+
-N, --short-names Shorten participant names like "First L"
|
|
57
|
+
Truncate chat names if --one-line
|
|
58
|
+
-T, --tiny Same as --short-names --one-line --urls
|
|
59
|
+
-k, --count Print total messages found at the end
|
|
60
|
+
-o, --capture [EXPR] Print only matched string, or EXPR
|
|
61
|
+
-j, --json JSON output
|
|
62
|
+
-P, --payload Include payload in JSON
|
|
63
|
+
-R, -z, --reverse Reverse sort order (oldest first)
|
|
64
|
+
|
|
65
|
+
Media:
|
|
66
|
+
-F, --files [KIND|KEY:PATTERN]
|
|
67
|
+
Find messages with files of KIND
|
|
68
|
+
or matching pattern for KEY
|
|
69
|
+
KIND: any (default) or images
|
|
70
|
+
KEY: name, ext, mime
|
|
71
|
+
-b, --list-files List attached files (default in normal list)
|
|
72
|
+
-B, --no-list-files Do not list attached files
|
|
73
|
+
-p, --preview [KIND] Inline image preview (requires terminal support)
|
|
74
|
+
KIND is some of: images, links; both if not given
|
|
75
|
+
-g, --img, --images Same as --files=images --preview=images
|
|
76
|
+
-Q, --silence-warnings Suppress warning messages and hints
|
|
77
|
+
|
|
78
|
+
Other:
|
|
79
|
+
--version Print version
|
|
80
|
+
--reset Clear and rebuild cache
|
|
81
|
+
-h, --help Show this help (see more with --help)
|
|
82
|
+
--help-dates Show detailed DATE help
|
|
83
|
+
|
|
84
|
+
~~LONG HELP BELOW~~
|
|
85
|
+
|
|
86
|
+
DATES
|
|
87
|
+
|
|
88
|
+
ISO8601 format (time/TZ optional): '2024-12-30', '2024-12-30 12:34'
|
|
89
|
+
|
|
90
|
+
Relative format: 4y (years), 1m5w (months+weeks), 30min, 3a (3am today)
|
|
91
|
+
|
|
92
|
+
Times default to local unless TZ specified or -Z used. See --help-dates for
|
|
93
|
+
details.
|
|
94
|
+
|
|
95
|
+
LOGICAL EXPRESSIONS
|
|
96
|
+
|
|
97
|
+
Conditions for --from, --chat, and --service are OR'd with others from same
|
|
98
|
+
option. --links, -Y, -X, -S too are all OR'd together.
|
|
99
|
+
|
|
100
|
+
Everything else is AND'ed by default. Chains of explicit --and/--or combine
|
|
101
|
+
as a single expression. Then get AND'ed with the rest. ANDs bind before ORs.
|
|
102
|
+
|
|
103
|
+
'-f x --or y' is shorthand for '-f x --or -f y'.
|
|
104
|
+
|
|
105
|
+
Mixing options is possible: '-fX -O -c -A -Mfoo'.
|
|
106
|
+
|
|
107
|
+
PATTERNS
|
|
108
|
+
|
|
109
|
+
PATTERN or CONTACT is a regular expression.
|
|
110
|
+
|
|
111
|
+
CONTACT matches address book information: You can give part of a name, phone
|
|
112
|
+
number, email. It doesn't matter which one a message was sent/received from.
|
|
113
|
+
|
|
114
|
+
Smart-case is the default case, which means case-insensitive, unless
|
|
115
|
+
uppercase letters present. Use -q to match literal strings, -i and -I to
|
|
116
|
+
change case sensitivity.
|
|
117
|
+
|
|
118
|
+
The flags -q, -x, -i, -I, and -C affect all subsequent patterns.
|
|
119
|
+
-e and -v affect the immediate next pattern only.
|
|
120
|
+
|
|
121
|
+
CAPTURE
|
|
122
|
+
|
|
123
|
+
EXPR is a format string for the message output where you can use capture
|
|
124
|
+
groups (alongside any text).
|
|
125
|
+
|
|
126
|
+
- When EXPR omitted, the entire match is presented. ('-o' = '-o"\0"')
|
|
127
|
+
- When it's a digit, that capture group is presented. ('-o1' = '-o"\1"')
|
|
128
|
+
- A comma-separated list of digits outputs those groups joined by space.
|
|
129
|
+
- When it's a format string: "foo \1 bar \2", interpolates the captures.
|
|
130
|
+
|
|
131
|
+
For a pattern to be available for capture, it must not be part of a logical
|
|
132
|
+
expression (--and/or) nor negated (-v).
|
|
133
|
+
|
|
134
|
+
If multiple message patterns are given, the last is used for capture.
|
|
135
|
+
|
|
136
|
+
FILES AND IMAGES
|
|
137
|
+
|
|
138
|
+
Inline image previews are possible on terminals that support it. We implement
|
|
139
|
+
iTerm and Kitty (Ghostty, etc) protocols.
|
|
140
|
+
|
|
141
|
+
Previews are available for links and image files. You can preview both (-p)
|
|
142
|
+
or either (-pi, -pl).
|
|
143
|
+
|
|
144
|
+
Filter files by:
|
|
145
|
+
--files # messages having any files at all
|
|
146
|
+
--files=images # with images
|
|
147
|
+
--files=ext:'jpe?g|png' # with jpeg, jpg, png extensions
|
|
148
|
+
--files=mime:video # video files
|
|
149
|
+
--files=name:'\bcats?\b' # files with word 'cat' or 'cats' in the name
|
|
150
|
+
|
|
151
|
+
JSON output does not include files by default. Use '-b' to include.
|
|
152
|
+
|
|
153
|
+
OTHER NOTES
|
|
154
|
+
|
|
155
|
+
'--to foo' will find a message sent by someone else to a group where foo
|
|
156
|
+
is a member. Use '--sent' will constrain to only messages you sent, and '-vc'
|
|
157
|
+
will exclude group chats entirely.
|
|
158
|
+
|
|
159
|
+
When listing links, using '--urls' is advisable so you get a shorter,
|
|
160
|
+
resolved (often canonical) URL.
|
|
161
|
+
|
|
162
|
+
Most things can be abbreviated, some combined:
|
|
163
|
+
--rev = --reverse
|
|
164
|
+
-Vi,r = --service=imessage --or --service=rcs
|
|
165
|
+
-Fn:bar,i = --files=name:bar --or --files=images
|
|
166
|
+
|
|
167
|
+
You cannot combine patterns by comma, but you can use '|': --from 'foo|bar'
|
|
168
|
+
This is generally simpler than using '--or' for same option.
|
|
169
|
+
|
|
170
|
+
Most things are regular expressions, even where you don't expect it.
|
|
171
|
+
Cf. file filter above.
|
|
172
|
+
|
|
173
|
+
EXAMPLES
|
|
174
|
+
|
|
175
|
+
PROG pattern # Message contents search
|
|
176
|
+
PROG -f Alice pattern # From Alice
|
|
177
|
+
PROG -d 2024-01-01 pattern # Since date
|
|
178
|
+
PROG -o '1,2' '(\w+):(\d+)' # Capture groups
|
|
179
|
+
PROG -eqIf andy@example.com -xit 'pedro|bob' -c
|
|
180
|
+
matches case-sensitive, literal, exact (whole email) andy@example.com
|
|
181
|
+
sender, and case-insensitive regexp recipient, and must be a group chat.
|
data/doc/HELP_DATES
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Date and Time Format Reference
|
|
2
|
+
|
|
3
|
+
This tool accepts flexible date and time inputs for filtering messages.
|
|
4
|
+
|
|
5
|
+
ABSOLUTE DATES
|
|
6
|
+
2024-01-01 Today's date in YYYY-MM-DD format
|
|
7
|
+
2024-1-1 Relaxed format (single digits OK)
|
|
8
|
+
2024-12-31 Any valid date
|
|
9
|
+
|
|
10
|
+
DATES WITH TIME
|
|
11
|
+
2024-01-01 10:30 Date with time (inherits your timezone, unless -Z)
|
|
12
|
+
2024-01-01T10:30 ISO format with T separator
|
|
13
|
+
2024-01-01 10:30:45 Include seconds
|
|
14
|
+
2024-01-01T10:30Z UTC timezone (Z suffix)
|
|
15
|
+
2024-01-01T10:30+05 Explicit timezone (short)
|
|
16
|
+
2024-01-01T10:30+05:30 Explicit timezone offset
|
|
17
|
+
2024-01-01T10:30-08:00 Negative timezone offset
|
|
18
|
+
|
|
19
|
+
TIME ONLY (uses today's date, respecting -Z)
|
|
20
|
+
1:34 24-hour format
|
|
21
|
+
23:59 24-hour format
|
|
22
|
+
10a, 10am 10 AM
|
|
23
|
+
10p, 10pm 10 PM
|
|
24
|
+
12:34a 12:34 AM (midnight hour)
|
|
25
|
+
12:45p 12:45 PM (noon hour)
|
|
26
|
+
0:34a 12:34 AM
|
|
27
|
+
1a 1 AM
|
|
28
|
+
1p 1 PM
|
|
29
|
+
|
|
30
|
+
RELATIVE DATES (how far back)
|
|
31
|
+
1d 1 day ago
|
|
32
|
+
7d 7 days ago
|
|
33
|
+
1w 1 week ago
|
|
34
|
+
2w 2 weeks ago
|
|
35
|
+
1m 1 month ago
|
|
36
|
+
6m 6 months ago
|
|
37
|
+
1y 1 year ago
|
|
38
|
+
2y 2 years ago
|
|
39
|
+
|
|
40
|
+
COMBINE RELATIVE DATES
|
|
41
|
+
1y6m 1 year 6 months ago
|
|
42
|
+
1y6m2w3d 1 year 6 months 2 weeks 3 days ago
|
|
43
|
+
6m1w 6 months 1 week ago
|
|
44
|
+
|
|
45
|
+
RELATIVE TIME
|
|
46
|
+
1h 1 hour ago
|
|
47
|
+
30M 30 minutes ago (capital M for minutes)
|
|
48
|
+
30min 30 minutes ago (alternative)
|
|
49
|
+
45s 45 seconds ago
|
|
50
|
+
2h30M 2 hours 30 minutes ago
|
|
51
|
+
1h30M45s 1 hour 30 minutes 45 seconds ago
|
|
52
|
+
|
|
53
|
+
COMBINE DATE AND TIME
|
|
54
|
+
3d2h 3 days 2 hours ago
|
|
55
|
+
1d12h30M 1 day 12 hours 30 minutes ago
|
|
56
|
+
5d3h15M30s 5 days 3 hours 15 minutes 30 seconds ago
|
|
57
|
+
1min2d 2 days 1 minute ago (order doesn't matter)
|
|
58
|
+
1d1d2m4m 6 months, 2 days ago (additive)
|
|
59
|
+
|
|
60
|
+
NOTES
|
|
61
|
+
- Optional minus sign: -1d same as 1d
|
|
62
|
+
- Case insensitive: 10AM, 10am, 10A all work
|
|
63
|
+
- M = minutes, m = months in relative times
|
|
64
|
+
- 12am = midnight, 12pm = noon
|
|
65
|
+
- Invalid dates will show an error
|
|
66
|
+
|
|
67
|
+
EXAMPLES
|
|
68
|
+
--since 2024-01-01 Messages after January 1, 2024
|
|
69
|
+
--until 3d Messages before 3 days ago
|
|
70
|
+
--since 1y6m Messages after 1 year 6 months ago
|
|
71
|
+
--until 10:30p Messages before 10:30 PM today
|
|
72
|
+
--since "2d 3h" Messages after 2 days 3 hours ago
|
data/ext/extconf.rb
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "mkmf"
|
|
4
|
+
|
|
5
|
+
# Check if dylib already exists
|
|
6
|
+
dylib_path = File.join *%W[ #{File.dirname __FILE__} .. lib imsg-grep images img2png.dylib ]
|
|
7
|
+
if File.exist?(dylib_path)
|
|
8
|
+
# Create no-op Makefile since dylib already exists
|
|
9
|
+
File.open("Makefile", "w") do |f|
|
|
10
|
+
f.puts <<~MAKEFILE
|
|
11
|
+
SHELL = /bin/sh
|
|
12
|
+
|
|
13
|
+
all:
|
|
14
|
+
\t@echo "dylib already exists, skipping build"
|
|
15
|
+
|
|
16
|
+
install:
|
|
17
|
+
\t@echo "dylib already exists, skipping install"
|
|
18
|
+
|
|
19
|
+
clean:
|
|
20
|
+
|
|
21
|
+
distclean:
|
|
22
|
+
\trm -f Makefile
|
|
23
|
+
|
|
24
|
+
.PHONY: all install clean distclean
|
|
25
|
+
MAKEFILE
|
|
26
|
+
end
|
|
27
|
+
exit 0
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Check for explicit disable via environment variable
|
|
31
|
+
images_disabled = %w[0 no false].include?(ENV['IMSGGREP_IMAGES']&.downcase)
|
|
32
|
+
|
|
33
|
+
# Check build requirements
|
|
34
|
+
|
|
35
|
+
is_darwin = RUBY_PLATFORM.include?("darwin")
|
|
36
|
+
found_swiftc = find_executable("swiftc")
|
|
37
|
+
|
|
38
|
+
has_requirements = is_darwin && found_swiftc
|
|
39
|
+
|
|
40
|
+
if has_requirements
|
|
41
|
+
# Create Swift compilation Makefile
|
|
42
|
+
File.open("Makefile", "w") do |f|
|
|
43
|
+
f.puts <<~MAKEFILE
|
|
44
|
+
SHELL = /bin/sh
|
|
45
|
+
|
|
46
|
+
DLLIB = img2png.dylib
|
|
47
|
+
|
|
48
|
+
all: $(DLLIB)
|
|
49
|
+
|
|
50
|
+
$(DLLIB): img2png.swift
|
|
51
|
+
\tswiftc -O -whole-module-optimization -lto=llvm-full -emit-library -D LIBRARY -o $(DLLIB) img2png.swift
|
|
52
|
+
|
|
53
|
+
install: $(DLLIB)
|
|
54
|
+
\tmkdir -p $(sitearchdir)/imsg-grep/images
|
|
55
|
+
\tcp $(DLLIB) $(sitearchdir)/imsg-grep/images/$(DLLIB)
|
|
56
|
+
|
|
57
|
+
clean:
|
|
58
|
+
\trm -f $(DLLIB)
|
|
59
|
+
|
|
60
|
+
distclean: clean
|
|
61
|
+
\trm -f Makefile
|
|
62
|
+
|
|
63
|
+
.PHONY: all install clean distclean
|
|
64
|
+
MAKEFILE
|
|
65
|
+
end
|
|
66
|
+
elsif images_disabled
|
|
67
|
+
# User explicitly disabled image support
|
|
68
|
+
File.open("Makefile", "w") do |f|
|
|
69
|
+
f.puts <<~MAKEFILE
|
|
70
|
+
SHELL = /bin/sh
|
|
71
|
+
|
|
72
|
+
all:
|
|
73
|
+
\t@echo "Image processing disabled via IMSGGREP_IMAGES environment variable"
|
|
74
|
+
|
|
75
|
+
install:
|
|
76
|
+
\t@echo "Image processing disabled via IMSGGREP_IMAGES environment variable"
|
|
77
|
+
|
|
78
|
+
clean:
|
|
79
|
+
|
|
80
|
+
distclean:
|
|
81
|
+
\trm -f Makefile
|
|
82
|
+
|
|
83
|
+
.PHONY: all install clean distclean
|
|
84
|
+
MAKEFILE
|
|
85
|
+
end
|
|
86
|
+
else
|
|
87
|
+
# Requirements not met - show helpful error and fail
|
|
88
|
+
$stderr.puts "ERROR: img2png extension build requirements not met:"
|
|
89
|
+
$stderr.puts " - Requires macOS (current platform: #{RUBY_PLATFORM})" unless is_darwin
|
|
90
|
+
$stderr.puts " - Requires swiftc (install Xcode or Swift toolchain)" unless found_swiftc
|
|
91
|
+
$stderr.puts ""
|
|
92
|
+
$stderr.puts "To build without image support, set:"
|
|
93
|
+
$stderr.puts " export IMSGGREP_IMAGES=0"
|
|
94
|
+
$stderr.puts ""
|
|
95
|
+
|
|
96
|
+
abort "Build failed due to missing requirements"
|
|
97
|
+
end
|