@@ -15,7 +15,10 @@ import { DeviceContext } from "../deviceContext";
1515import { IArduinoSettings } from "./arduinoSettings" ;
1616import { BoardManager } from "./boardManager" ;
1717import { ExampleManager } from "./exampleManager" ;
18- import { ICoCoPaContext , isCompilerParserEnabled , makeCompilerParserContext } from "./intellisense" ;
18+ import { AnalysisManager ,
19+ ICoCoPaContext ,
20+ isCompilerParserEnabled ,
21+ makeCompilerParserContext } from "./intellisense" ;
1922import { LibraryManager } from "./libraryManager" ;
2023import { VscodeSettings } from "./vscodeSettings" ;
2124
@@ -25,6 +28,11 @@ import { SerialMonitor } from "../serialmonitor/serialMonitor";
2528import { UsbDetector } from "../serialmonitor/usbDetector" ;
2629import { ProgrammerManager } from "./programmerManager" ;
2730
31+ /**
32+ * Supported build modes. For further explanation see the documentation
33+ * of ArduinoApp.build().
34+ * The strings are used for status reporting within the above function.
35+ */
2836export enum BuildMode {
2937 Verify = "Verifying" ,
3038 Analyze = "Analyzing" ,
@@ -45,10 +53,30 @@ export class ArduinoApp {
4553
4654 private _programmerManager : ProgrammerManager ;
4755
56+ /**
57+ * IntelliSense analysis manager.
58+ * Makes sure that analysis builds and regular builds go along
59+ * and that multiple subsequent analysis requests - as triggered
60+ * by board/board-configuration changes - are bundled to a single
61+ * analysis build run.
62+ */
63+ private _analysisManager : AnalysisManager ;
64+
65+ /**
66+ * Indicates if a build is currently in progress.
67+ * If so any call to this.build() will return false immediately.
68+ */
69+ private _building : boolean = false ;
70+
4871 /**
4972 * @param {IArduinoSettings } _settings ArduinoSetting object.
5073 */
5174 constructor ( private _settings : IArduinoSettings ) {
75+ const analysisDelayMs = 1000 * 3 ;
76+ this . _analysisManager = new AnalysisManager (
77+ ( ) => this . _building ,
78+ async ( ) => { await this . build ( BuildMode . Analyze , true ) ; } ,
79+ analysisDelayMs ) ;
5280 }
5381
5482 /**
@@ -71,6 +99,17 @@ export class ArduinoApp {
7199 } catch ( ex ) {
72100 }
73101 }
102+
103+ // set up event handling for IntelliSense analysis
104+ const requestAnalysis = async ( ) => {
105+ if ( isCompilerParserEnabled ( ) ) {
106+ await this . _analysisManager . requestAnalysis ( ) ;
107+ }
108+ } ;
109+ const dc = DeviceContext . getInstance ( ) ;
110+ dc . onChangeBoard ( requestAnalysis ) ;
111+ dc . onChangeConfiguration ( requestAnalysis ) ;
112+ dc . onChangeSketch ( requestAnalysis ) ;
74113 }
75114
76115 /**
@@ -101,38 +140,96 @@ export class ArduinoApp {
101140 }
102141 }
103142
143+ /**
144+ * Returns true if a build is currently in progress.
145+ */
146+ public get building ( ) {
147+ return this . _building ;
148+ }
149+
104150 /**
105151 * Runs the arduino builder to build/compile and - if necessary - upload
106152 * the current sketch.
107- * @param buildMode Build mode. BuildMode.Upload: Compile and upload,
108- * BuildMode.UploadProgrammer: Compile and upload using the user selectable
109- * programmer, BuildMode.Analyze: Compile, analyze the output and generate
110- * IntelliSense configuration from it, BuildMode.Verify: Just compile.
153+ * @param buildMode Build mode.
154+ * * BuildMode.Upload: Compile and upload
155+ * * BuildMode.UploadProgrammer: Compile and upload using the user
156+ * selectable programmer
157+ * * BuildMode.Analyze: Compile, analyze the output and generate
158+ * IntelliSense configuration from it.
159+ * * BuildMode.Verify: Just compile.
160+ * All build modes except for BuildMode.Analyze run interactively, i.e. if
161+ * something is missing, it tries to query the user for the missing piece
162+ * of information (sketch, board, etc.). Analyze runs non interactively and
163+ * just returns false.
111164 * @param {bool } compile - Indicates whether to compile the code when using the CLI to upload
112165 * @param buildDir Override the build directory set by the project settings
113166 * with the given directory.
167+ * @returns true on success, false if
168+ * * another build is currently in progress
169+ * * board- or programmer-manager aren't initialized yet
170+ * * or something went wrong during the build
114171 */
115- public async build ( buildMode : BuildMode , compile : boolean , buildDir ?: string ) : Promise < boolean > {
172+ public async build ( buildMode : BuildMode , compile : boolean , buildDir ?: string ) {
173+
174+ if ( ! this . _boardManager || ! this . _programmerManager || this . _building ) {
175+ return false ;
176+ }
177+
178+ this . _building = true ;
179+
180+ return await this . _build ( buildMode , compile , buildDir )
181+ . then ( ( ret ) => {
182+ this . _building = false ;
183+ return ret ;
184+ } )
185+ . catch ( ( reason ) => {
186+ this . _building = false ;
187+ // TODO EW, 2020-02-19: Report unhandled error (Logger?)
188+ return false ;
189+ } ) ;
190+ }
191+
192+ // Not moving _build around in the file (yet?) because it would create too much merge/rebase problems.
193+ /* tslint:disable:member-ordering */
194+
195+ /**
196+ * Private implementation. Not to be called directly. The wrapper build()
197+ * manages the build state.
198+ * @param buildMode See build()
199+ * @param buildDir See build()
200+ */
201+ public async _build ( buildMode : BuildMode , compile : boolean , buildDir ?: string ) : Promise < boolean > {
116202 const dc = DeviceContext . getInstance ( ) ;
117203 const args : string [ ] = [ ] ;
118204 let restoreSerialMonitor : boolean = false ;
119205 let cocopa : ICoCoPaContext ;
120206
121- const boardDescriptor = this . getBoardBuildString ( ) ;
122- if ( ! boardDescriptor ) {
207+ if ( ! this . boardManager . currentBoard ) {
208+ if ( buildMode !== BuildMode . Analyze ) {
209+ logger . notifyUserError ( "boardManager.currentBoard" , new Error ( constants . messages . NO_BOARD_SELECTED ) ) ;
210+ }
123211 return false ;
124212 }
213+ const boardDescriptor = this . boardManager . currentBoard . getBuildConfig ( ) ;
214+
125215 if ( ! this . useArduinoCli ( ) ) {
126216 args . push ( "--board" , boardDescriptor ) ;
127217 }
128218
129219 if ( ! ArduinoWorkspace . rootPath ) {
130- vscode . window . showWarningMessage ( "Cannot find the sketch file ." ) ;
220+ vscode . window . showWarningMessage ( "Workspace doesn't seem to have a folder added to it yet ." ) ;
131221 return false ;
132222 }
133223
134224 if ( ! dc . sketch || ! util . fileExistsSync ( path . join ( ArduinoWorkspace . rootPath , dc . sketch ) ) ) {
135- await this . getMainSketch ( dc ) ;
225+ if ( buildMode === BuildMode . Analyze ) {
226+ // Analyze runs non interactively
227+ return false ;
228+ }
229+ if ( ! await dc . resolveMainSketch ( ) ) {
230+ vscode . window . showErrorMessage ( "No sketch file was found. Please specify the sketch in the arduino.json file" ) ;
231+ return false ;
232+ }
136233 }
137234
138235 const selectSerial = async ( ) => {
@@ -171,8 +268,9 @@ export class ArduinoApp {
171268 args . push ( "--port" , dc . port ) ;
172269 }
173270 } else if ( buildMode === BuildMode . UploadProgrammer ) {
174- const programmer = this . getProgrammerString ( ) ;
271+ const programmer = this . programmerManager . currentProgrammer ;
175272 if ( ! programmer ) {
273+ logger . notifyUserError ( "programmerManager.currentProgrammer" , new Error ( constants . messages . NO_PROGRAMMMER_SELECTED ) ) ;
176274 return false ;
177275 }
178276 if ( ! dc . port ) {
@@ -205,9 +303,6 @@ export class ArduinoApp {
205303
206304 args . push ( "--port" , dc . port ) ;
207305 } else if ( buildMode === BuildMode . Analyze ) {
208- if ( ! isCompilerParserEnabled ( ) ) {
209- return false ;
210- }
211306 cocopa = makeCompilerParserContext ( dc ) ;
212307 if ( ! this . useArduinoCli ( ) ) {
213308 args . push ( "--verify" , "--verbose" ) ;
@@ -229,6 +324,8 @@ export class ArduinoApp {
229324
230325 await vscode . workspace . saveAll ( false ) ;
231326
327+ // we prepare the channel here since all following code will
328+ // or at leas can possibly output to it
232329 arduinoChannel . show ( ) ;
233330 arduinoChannel . start ( `${ buildMode } sketch '${ dc . sketch } '` ) ;
234331
@@ -716,7 +813,7 @@ export class ArduinoApp {
716813 const dc = DeviceContext . getInstance ( ) ;
717814 const arduinoJson = {
718815 sketch : sketchFile ,
719- // TODO: COM1 is Windows specific - what about OSX and Linux users?
816+ // TODO EW, 2020-02-18 : COM1 is Windows specific - what about OSX and Linux users?
720817 port : dc . port || "COM1" ,
721818 board : dc . board ,
722819 configuration : dc . configuration ,
@@ -818,14 +915,15 @@ export class ArduinoApp {
818915 * @returns True if successful, false on error.
819916 */
820917 protected async runPreBuildCommand ( dc : DeviceContext ) : Promise < boolean > {
821- if ( dc . prebuild ) {
822- arduinoChannel . info ( `Running pre-build command: ${ dc . prebuild } ` ) ;
823- const prebuildargs = dc . prebuild . split ( " " ) ;
824- const prebuildCommand = prebuildargs . shift ( ) ;
918+ const prebuildcmdline = dc . prebuild ;
919+ if ( prebuildcmdline ) {
920+ arduinoChannel . info ( `Running pre-build command: ${ prebuildcmdline } ` ) ;
921+ const args = prebuildcmdline . split ( / \s + / ) ;
922+ const cmd = args . shift ( ) ;
825923 try {
826- await util . spawn ( prebuildCommand ,
924+ await util . spawn ( cmd ,
827925 arduinoChannel . channel ,
828- prebuildargs ,
926+ args ,
829927 { shell : true , cwd : ArduinoWorkspace . rootPath } ) ;
830928 } catch ( ex ) {
831929 arduinoChannel . error ( `Running pre-build command failed: ${ os . EOL } ${ ex . error } ` ) ;
@@ -843,30 +941,5 @@ export class ArduinoApp {
843941 return this . _settings . useArduinoCli ;
844942 // return VscodeSettings.getInstance().useArduinoCli;
845943 }
846-
847- private getProgrammerString ( ) : string {
848- const selectProgrammer = this . programmerManager . currentProgrammer ;
849- if ( ! selectProgrammer ) {
850- logger . notifyUserError ( "getProgrammerString" , new Error ( constants . messages . NO_PROGRAMMMER_SELECTED ) ) ;
851- return ;
852- }
853- return selectProgrammer ;
854- }
855-
856- private getBoardBuildString ( ) : string {
857- const selectedBoard = this . boardManager . currentBoard ;
858- if ( ! selectedBoard ) {
859- logger . notifyUserError ( "getBoardBuildString" , new Error ( constants . messages . NO_BOARD_SELECTED ) ) ;
860- return ;
861- }
862- return selectedBoard . getBuildConfig ( ) ;
863- }
864-
865- private async getMainSketch ( dc : DeviceContext ) {
866- await dc . resolveMainSketch ( ) ;
867- if ( ! dc . sketch ) {
868- vscode . window . showErrorMessage ( "No sketch file was found. Please specify the sketch in the arduino.json file" ) ;
869- throw new Error ( "No sketch file was found." ) ;
870- }
871- }
944+ /* tslint:enable:member-ordering */
872945}
0 commit comments