aboutsummaryrefslogtreecommitdiff
path: root/bin/ardmk-init
blob: 461e9ab6e2fade3987121f86683131f5d310434a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#!/usr/bin/env python
"""
Arduino-mk Makefile and project initialiser

This script can be used in its basic form create a project specific Makefile
for use with Arduino-mk. Addionally, it can be used to create a template
Arduino source file and a traditional boilerplate project file structure.

Example:
    * Run prompted within current working directory: `ardmk-init`
    * Create Arduino Uno Makefile (useful within a library example): `ardmk-init -qb uno`
    * Create boilerplate Arduino Uno project in current working directory of same
      name: `ardmk-init -b uno --quiet --project`
    * Create Arduino-mk nano Makefile in current working directory with template .ino:
    `ardmk-init -b nano -u atmega328 -qtn my-project`

See `armk-init --help` for CLI arguments
"""

from __future__ import print_function
import os
import argparse

## Global Vars
VERSION = "1.0"
ARD_TEMPLATE = "\n\
#include <Arduino.h>\n\
#include <Wire.h>\n\
\n\
\n\
void setup() {\n\
}\n\
\n\
void loop() {\n\
}\n\
"

## Command Parser
PARSER = argparse.ArgumentParser(description='Arduino Makefile and boilerplate project generator.\
        For use with Ard-Makefile: https://github.com/sudar/Arduino-Makefile.\
        Script created by John Whittington https://github.com/tuna-f1sh 2017\
        \n\nVersion: ' + VERSION, usage='ardmk-init # prompted CLI, see --help for more.')
PARSER.add_argument('-v', '--verbose', action='store_true',
                    help="print file contents during creation")
PARSER.add_argument('-d', '--directory', default=os.getcwd(), help='directory to run generator')
PARSER.add_argument('-b', '--board', default='uno', help='board tag')
PARSER.add_argument('-u', '--micro', default='AUTO', help='microcontroller on board')
PARSER.add_argument('-f', '--freq', default='AUTO', help='clock frequency')
PARSER.add_argument('-p', '--port', default='AUTO', help='monitor port')
PARSER.add_argument('-n', '--name', default=os.path.basename(os.getcwd()), help='project name')
PARSER.add_argument('-q', '--quiet', action='store_true', help='run quiet without user prompts')
PARSER.add_argument('-P', '--project', action='store_true',
                    help='create boilerplate project with src, lib and bin folder structure')
PARSER.add_argument('-t', '--template', action='store_true',
                    help='create bare minimum Arduino source file')
ARGS = PARSER.parse_args()

try:
    from clint.textui import prompt, validators
except ImportError:
    if not ARGS.quiet:
        print("Python module 'clint' is required for running prompted. Install the module or run with arguments only using --quiet")
        quit()


def generate_makefile():
    """
    Generate the Makefile content using prompts or parsed arguments
    """
    # Header
    file_content = "# Generated by ard-make version " + VERSION + "\n\n"

    # Basic
    if not ARGS.quiet:
        print("Generating Arduino Ard-Makefile project in "
                          + os.path.abspath(ARGS.directory))
        btag = prompt.query('Board tag?', default='uno')
        if btag != 'uno':
            bsub = prompt.query('Board sub micro?', default='atmega328')
            f_cpu = prompt.query('Board frequency', default='16000000L')
        else:
            bsub = 'AUTO'
            f_cpu = 'AUTO'
        monitor_port = prompt.query('Arduino port?', default='AUTO')
    else:
        btag = ARGS.board
        bsub = ARGS.micro
        f_cpu = ARGS.freq
        monitor_port = ARGS.port

    file_content += check_define('BOARD_TAG', btag)
    file_content += check_define('BOARD_SUB', bsub)
    file_content += check_define('F_CPU', f_cpu)
    file_content += check_define('MONITOR_PORT', monitor_port)

    # Extended
    if not ARGS.quiet:
        if not prompt.yn('Extended options?', default='n'):
            if not prompt.yn('Define local folders?', default='n'):
                src_dir = prompt.query('Sources folder (Makefile will be created here)?',\
                                default='', validators=[])
                userlibs = prompt.query('Library folder (will create if does not exist) - AUTO is Sketchbook directory?',
                                default='AUTO', validators=[])
                obj_dir = prompt.query('Output directory?', default='AUTO', validators=[])
            else:
                src_dir = ''
                userlibs = 'AUTO'
                obj_dir = 'AUTO'
            boards_txt = prompt.query('Boards file?', default='AUTO')
            isp_prog = prompt.query('ISP programmer?', default='atmelice_isp')
            isp_port = prompt.query('ISP port?', default='AUTO')
            if not prompt.yn('Quiet make?', default='n'):
                file_content += "ARDUINO_QUIET = 1\n"

            file_content += check_define('ISP_PROG', isp_prog)
            file_content += check_define('ISP_PORT', isp_port)
            file_content += check_define('BOARDS_TXT', boards_txt)

            # Check andd create folders
            check_create_folder(src_dir)
            check_create_folder(userlibs)
            check_create_folder(obj_dir)

            # Makefile will be in src_dir so lib and bin must be relative
            if src_dir:
                userlibs = "../" + userlibs
                obj_dir = "../" + obj_dir

            file_content += check_define('USER_LIB_PATH', userlibs)
            file_content += check_define('OBJDIR', obj_dir)
        else:
            src_dir = ''

        if ARGS.template or not prompt.yn('Create template Arduino source?', default='n'):
            source_filename = prompt.query('Name of project?',
                                           default=os.path.basename(os.getcwd()))
            if src_dir:
                write_template(src_dir + "/" + source_filename)
            else:
                write_template(source_filename)
            file_content += check_define('TARGET', source_filename)

    else:
        if ARGS.project:
            src_dir = 'src'
            userlibs = 'lib'
            obj_dir = 'bin'
        else:
            src_dir = ''
            userlibs = 'AUTO'
            obj_dir = 'AUTO'

        # Check andd create folders
        check_create_folder(src_dir)
        check_create_folder(userlibs)
        check_create_folder(obj_dir)

        # Makefile will be in src_dir so lib and bin must be relative
        if src_dir:
            userlibs = "../" + userlibs
            obj_dir = "../" + obj_dir

        file_content += check_define('USER_LIB_PATH', userlibs)
        file_content += check_define('OBJDIR', obj_dir)

        if ARGS.project or ARGS.template:
            if src_dir:
                write_template(src_dir + "/" + ARGS.name)
            else:
                write_template(ARGS.name)
            file_content += check_define('TARGET', ARGS.name)

    if not "ARDMK_DIR" in os.environ:
        if ARGS.quiet:
            puts(colored.magenta('Warning: ARDMK_DIR environment variable not defined. \
                        Must be defined for Makefile to work'))
        else:
            ardmk = prompt.query('Arduino Makefile path?',
                                 default='/usr/share/arduino',
                                 validators=[validators.PathValidator()])
            ardmk = "ARDMK_DIR := " + ardmk + "\n"

    file_content += "\ninclude $(ARDMK_DIR)/Arduino.mk"

    # Add forward slash if source directory exists
    if src_dir:
        write_to_makefile(file_content, (src_dir + "/"))
    else:
        write_to_makefile(file_content, "")

    return file_content

def write_to_makefile(file_content, path):
    """
    Write the Makefile file
    """
    makefile = open(path + "Makefile", 'w')
    print("Writing Makefile...")
    if ARGS.verbose:
        print(file_content)
    makefile.write(file_content)
    makefile.close()

def write_template(filename):
    """
    Write template Arduino .ino source
    """
    print("Writing " + os.path.abspath(filename) + ".ino...")
    if os.path.isfile(filename + '.ino'):
        if ARGS.quiet:
            print(filename + '.ino' + ' already exists! Stopping.')
            return
        print(filename + '.ino' + ' already exists! Overwrite?')
        if prompt.yn('Continue?', default='n'):
            return
    src = open((filename + ".ino"), 'w')
    if ARGS.verbose:
        print(ARD_TEMPLATE)
    src.write("/* Project: " + filename + " */\n" + ARD_TEMPLATE)
    src.close()

def check_create_folder(folder):
    """
    Check if folder exists and make it if it doesn't and hasn't been set to AUTO
    """
    if folder and not folder == 'AUTO':
        if not os.path.exists(folder):
            print("Creating " + os.path.abspath(folder) + " folder")
            os.makedirs(folder)

def check_define(define, user):
    """
    Check whether user has set define and return Makefile formatted string if they have
    """
    # Return is empty unless user has passed value
    string = ""

    # Set define only if not empty or set to AUTO
    if user and not user == 'AUTO':
        string = define + " = " + user + "\n"

    return string

if __name__ == '__main__':
    # Create directory if not exist
    check_create_folder(ARGS.directory)
    # Change to dir so all commands are run relative
    os.chdir(ARGS.directory)
    if os.path.isfile('Makefile'):
        if ARGS.quiet:
            print('Makefile in ' + os.path.abspath(ARGS.directory)
                             + ' already exists! Stopping.')
            quit()

        # Confirm with user if not quiet mode
        print('Makefile in ' + os.path.abspath(ARGS.directory)
                         + ' already exists! Overwrite?')
        if prompt.yn('Continue?', default='n'):
            quit()
    # Run it
    generate_makefile()