1 // clone_repo_dialog.cpp : implementation file
2 //
3
4 #include "studiox_includes.h"
5 #include "clone_repo_dialog.h"
6
7 #ifdef _DEBUG
8 #define new DEBUG_NEW
9 #endif
10
11 char guix_repo_url[] = "https://github.com/eclipse-threadx/guix.git";
12
13 BEGIN_MESSAGE_MAP(clone_repo_dialog, express_dialog)
14 ON_WM_CLOSE()
15 ON_BN_CLICKED(IDB_EXIT_CLONE_DIALOG, OnCancel)
16 ON_BN_CLICKED(IDB_CLONE_REPO, OnCloneRepo)
17 ON_BN_CLICKED(IDB_SELECT_DIRECTORY, OnSetDirectory)
18 ON_MESSAGE(USR_MSG_REPO_CLONE_MSG_UPDATE, OnRepoCloneMsgUpdate)
19 END_MESSAGE_MAP()
20
21
22 IMPLEMENT_DYNAMIC(clone_repo_dialog, express_dialog)
23
24 #define GIT_CLONE_MSG_UPDATE_PERIOD 100 /* ms */
25 #define MSG_TYPE_CLONE 0
26 #define MSG_TYPE_COMPLETE 1
27
28 struct git_clone_info {
29 char msg[MAX_PATH];
30 int msg_type;
31 BOOL msg_updated;
32 git_indexer_progress stats;
33 BOOL stats_updated;
34 int checkout_completed;
35 int checkout_total;
36 BOOL checkout_updated;
37 };
38
39 extern INI_INFO StudioXIni;
40 extern CString target_class_name;
41
42 static git_clone_info CloneInfo;
43
44 ///////////////////////////////////////////////////////////////////////////////
WriteCloneInfoToPipe(HANDLE writePipe,git_clone_info * info)45 static BOOL WriteCloneInfoToPipe(HANDLE writePipe, git_clone_info *info)
46 {
47 if (writePipe)
48 {
49 DWORD writtenSize;
50 BOOL result;
51
52 result = WriteFile(writePipe, info, sizeof(git_clone_info), &writtenSize, NULL);
53 if (writtenSize == sizeof(git_clone_info) && result)
54 {
55 info->checkout_updated = FALSE;
56 info->msg_updated = FALSE;
57 info->stats_updated = FALSE;
58 return TRUE;
59 }
60 }
61
62 return FALSE;
63 }
64
65 ///////////////////////////////////////////////////////////////////////////////
ReadCloneInfoFromPipe(HANDLE readPipe,git_clone_info * info)66 static BOOL ReadCloneInfoFromPipe(HANDLE readPipe, git_clone_info *info)
67 {
68 DWORD readSize;
69
70 if (readPipe)
71 {
72 BOOL result;
73 result = ReadFile(readPipe, info, sizeof(git_clone_info), &readSize, NULL);
74
75 if (readSize == sizeof(git_clone_info) && result)
76 {
77 return TRUE;
78 }
79 }
80
81 return FALSE;
82 }
83
84 ///////////////////////////////////////////////////////////////////////////////
clone_repo_dialog(CWnd * parent)85 clone_repo_dialog::clone_repo_dialog(CWnd *parent)
86 : express_dialog(clone_repo_dialog::IDD, parent)
87 {
88 IconId = IDB_WARNING;
89 SetTitleText(CString("Clone GUIX Repository"));
90
91 ZeroMemory(&mProcessInfo, sizeof(PROCESS_INFORMATION));
92 mpProgressScreen = NULL;
93 mWritePipeHandle = 0;
94 mReadPipeHandle = 0;
95 }
96
97 ///////////////////////////////////////////////////////////////////////////////
OnInitDialog()98 BOOL clone_repo_dialog::OnInitDialog()
99 {
100 express_dialog::OnInitDialog();
101 AddCancelButton();
102 return TRUE; // return TRUE unless you set the focus to a control
103 }
104
105 ///////////////////////////////////////////////////////////////////////////////
OnClose()106 void clone_repo_dialog::OnClose()
107 {
108 CDialog::OnClose();
109 }
110
111 ///////////////////////////////////////////////////////////////////////////////
OnCancel()112 void clone_repo_dialog::OnCancel()
113 {
114 if (mpProgressScreen)
115 {
116 CompleteGUIXRepoClone(L"GUIX repository clone aborted!");
117 }
118 else
119 {
120 express_dialog::OnCancel();
121 }
122 }
123
124 ///////////////////////////////////////////////////////////////////////////////
guix_transport_message_cb(const char * str,int len,void * payload)125 int guix_transport_message_cb(const char *str, int len, void *payload)
126 {
127 if (len >= MAX_PATH)
128 {
129 len = MAX_PATH - 1;
130 }
131
132 memcpy_s(CloneInfo.msg, MAX_PATH, str, len);
133 CloneInfo.msg[len] = 0;
134
135 CloneInfo.msg_type = MSG_TYPE_CLONE;
136 CloneInfo.msg_updated = TRUE;
137
138 return 0;
139 }
140
141 ///////////////////////////////////////////////////////////////////////////////
guix_indexer_progress_cb(const git_indexer_progress * stats,void * payload)142 int guix_indexer_progress_cb(const git_indexer_progress *stats, void *payload)
143 {
144 CloneInfo.stats = *stats;
145 CloneInfo.stats_updated = TRUE;
146
147 return 0;
148 }
149
150 ///////////////////////////////////////////////////////////////////////////////
guix_checkout_progress_cb(const char * path,size_t completed_steps,size_t total_steps,void * payload)151 void guix_checkout_progress_cb(const char *path, size_t completed_steps, size_t total_steps, void *payload)
152 {
153 CloneInfo.checkout_completed = completed_steps;
154 CloneInfo.checkout_total = total_steps;
155 CloneInfo.checkout_updated = TRUE;
156 }
157
158 ///////////////////////////////////////////////////////////////////////////////
OnWriteCloneInfoTimer(void * lpParametar,BOOLEAN TimerOrWaitFired)159 void CALLBACK OnWriteCloneInfoTimer(void* lpParametar, BOOLEAN TimerOrWaitFired)
160 {
161 HANDLE write_pipe_handle = (HANDLE)lpParametar;
162
163 if (CloneInfo.msg_updated ||
164 CloneInfo.checkout_updated ||
165 CloneInfo.stats_updated)
166 {
167 WriteCloneInfoToPipe(write_pipe_handle, &CloneInfo);
168 }
169 }
170
171 ///////////////////////////////////////////////////////////////////////////////
CloneGUIXRepo(char * local_path,HANDLE write_pipe_handle)172 void CloneGUIXRepo(char *local_path, HANDLE write_pipe_handle)
173 {
174 BOOL clone_success = FALSE;
175 git_repository* clone_out;
176 git_clone_options options;
177 git_remote_callbacks remote_callbacks;
178 git_checkout_options checkout_opts;
179 git_fetch_options fetch_options = GIT_FETCH_OPTIONS_INIT;
180
181 git_libgit2_init();
182 git_clone_options_init(&options, GIT_CLONE_OPTIONS_VERSION);
183
184 remote_callbacks = fetch_options.callbacks;
185 remote_callbacks.sideband_progress = guix_transport_message_cb;
186 remote_callbacks.transfer_progress = guix_indexer_progress_cb;
187 fetch_options.callbacks = remote_callbacks;
188 options.fetch_opts = fetch_options;
189
190 checkout_opts = options.checkout_opts;
191 checkout_opts.progress_cb = guix_checkout_progress_cb;
192 options.checkout_opts = checkout_opts;
193
194 CloneInfo.checkout_updated = FALSE;
195 CloneInfo.msg_updated = FALSE;
196 CloneInfo.stats_updated = FALSE;
197
198 // Create a timer to write git clone information to named pipe memory.
199 HANDLE hTimer;
200 BOOL success = ::CreateTimerQueueTimer(
201 &hTimer,
202 NULL,
203 OnWriteCloneInfoTimer,
204 (PVOID)write_pipe_handle,
205 GIT_CLONE_MSG_UPDATE_PERIOD,
206 GIT_CLONE_MSG_UPDATE_PERIOD,
207 WT_EXECUTEINTIMERTHREAD);
208
209
210 int clone_status = git_clone(&clone_out, guix_repo_url, local_path, &options);
211
212 // Delete timer
213 DeleteTimerQueueTimer(NULL, hTimer, NULL);
214
215 const git_error* error = NULL;
216 if (clone_status)
217 {
218 error = git_error_last();
219 }
220 else
221 {
222 git_repository_free(clone_out);
223 }
224
225 if(error)
226 {
227 memcpy_s(CloneInfo.msg, MAX_PATH, error->message, strnlen(error->message, MAX_PATH - 1) + 1);
228 }
229 else
230 {
231 CloneInfo.msg[0] = '\0';
232 }
233
234 git_libgit2_shutdown();
235
236 // Nofity the calling process that git clone is completed.
237 CloneInfo.msg_type = MSG_TYPE_COMPLETE;
238 CloneInfo.msg_updated = TRUE;
239 WriteCloneInfoToPipe(write_pipe_handle , &CloneInfo);
240 }
241
242 ///////////////////////////////////////////////////////////////////////////////
ReadCloneInfoThreadEntry(LPVOID thread_input)243 static DWORD WINAPI ReadCloneInfoThreadEntry(LPVOID thread_input)
244 {
245
246 clone_repo_dialog *parent = (clone_repo_dialog *)thread_input;
247 HANDLE read_pipe_handle = parent->GetReadPipeHandle();
248
249 git_clone_info info;
250
251 while (1)
252 {
253 if (ReadCloneInfoFromPipe(read_pipe_handle, &info))
254 {
255 parent->SendMessage(USR_MSG_REPO_CLONE_MSG_UPDATE, (WPARAM)&info, NULL);
256 }
257 }
258 }
259
260 ///////////////////////////////////////////////////////////////////////////////
OnCloneRepo()261 void clone_repo_dialog::OnCloneRepo()
262 {
263 TCHAR folder_path[MAX_PATH];
264 char *repo_url = guix_repo_url;
265
266 if (BrowseForFolder(this->GetSafeHwnd(), _T("Select root for GUIX git repository clone..."), _T("C:\\Eclipse_ThreadX"), folder_path))
267 {
268 //"git clone --depth 1 aka.ms/azrtos-guix-repo --branch master --single-branch"
269
270 CString CloneMessage(_T("Cloning from source URL "));
271 CloneMessage += repo_url;
272 CloneMessage += " to\n";
273 CloneMessage += "local folder ";
274 CloneMessage += folder_path;
275 CloneMessage += "\\guix";
276
277 mpProgressScreen = new git_progress_screen(this, CloneMessage);
278 mpProgressScreen->Create(IDD_GIT_PROGRESS, this);
279 mpProgressScreen->ShowWindow(SW_SHOW);
280
281 ShowHideChildren(SW_HIDE);
282
283 mLocalPath.Format(L"%s\\guix", folder_path);
284
285 // Run a subprocess to execute guix repo clone
286 TCHAR exename[MAX_PATH];
287
288 GetModuleFileName(NULL, exename, MAX_PATH);
289
290 SECURITY_ATTRIBUTES security_attri;
291
292 security_attri.nLength = sizeof(SECURITY_ATTRIBUTES);
293 security_attri.lpSecurityDescriptor = NULL;
294 security_attri.bInheritHandle = TRUE;
295
296 if(!CreatePipe(&mReadPipeHandle, &mWritePipeHandle, &security_attri, 0))
297 {
298 ErrorMsg("Create git clone named pipe memory failed!", this);
299 return;
300 }
301
302 // Start a work thread to read git clone message from pipe.
303 mReadCloneInfoThreadHandle = CreateThread(NULL, GX_WIN32_STACK_SIZE, (LPTHREAD_START_ROUTINE)ReadCloneInfoThreadEntry, (LPVOID)this, 0, 0);
304
305 if (mReadCloneInfoThreadHandle == INVALID_HANDLE_VALUE)
306 {
307 ErrorMsg("Create thread failed!", this);
308 mReadCloneInfoThreadHandle = 0;
309 return;
310 }
311
312 CString szCmdLine;
313 szCmdLine.Format(L"%s -n --clone_guix_repo \"%s\" --write_pipe_handle %d", exename, mLocalPath, mWritePipeHandle);
314
315 STARTUPINFO siStartInfo;
316
317 // Set up members of the PROCESS_INFORMATION structure.
318 ZeroMemory(&mProcessInfo, sizeof(PROCESS_INFORMATION));
319
320 // Set up members of the STARTUPINFO structure.
321 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
322 siStartInfo.cb = sizeof(STARTUPINFO);
323
324 // Create the child process.
325 if(!CreateProcess(NULL,
326 szCmdLine.GetBuffer(), // command line
327 NULL, // process security attributes
328 NULL, // primary thread security attributes
329 TRUE, // handles are inherited
330 0, // creation flags
331 NULL, // use parent's environment
332 NULL, // use parent's current directory
333 &siStartInfo, // STARTUPINFO pointer
334 &mProcessInfo // receives PROCESS_INFORMATION
335 ))
336 {
337 ErrorMsg("Create git clone process failed!", this);
338 return;
339 }
340 }
341 }
342
343 ///////////////////////////////////////////////////////////////////////////////
CompleteGUIXRepoClone(CString msg)344 void clone_repo_dialog::CompleteGUIXRepoClone(CString msg)
345 {
346 // Terminate git clone process
347 TerminateProcess(mProcessInfo.hProcess, 0);
348
349 // Close handles to the child process and its primary thread.
350 CloseHandle(mProcessInfo.hProcess);
351 CloseHandle(mProcessInfo.hThread);
352 ZeroMemory(&mProcessInfo, sizeof(PROCESS_INFORMATION));
353
354 // Close read and write pipe handles.
355 if (mReadPipeHandle)
356 {
357 CloseHandle(mReadPipeHandle);
358 mReadPipeHandle = 0;
359 }
360
361 if (mWritePipeHandle)
362 {
363 CloseHandle(mWritePipeHandle);
364 mWritePipeHandle = 0;
365 }
366
367 // Terminate clone information read thread.
368 if (mReadCloneInfoThreadHandle)
369 {
370 TerminateThread(mReadCloneInfoThreadHandle, 0);
371 CloseHandle(mReadCloneInfoThreadHandle);
372 mReadCloneInfoThreadHandle = 0;
373 }
374
375 BOOL clone_succeed = FALSE;
376
377 if (!msg.IsEmpty())
378 {
379 ErrorMsg(CW2A(msg), this);
380 }
381 else
382 {
383 StudioXIni.samples_dir = mLocalPath;
384 WriteIniInfo();
385 clone_repo_dialog::PopulateRecentProjects(StudioXIni.samples_dir);
386 Notify("The GUIX sample projects are now installed.", this);
387 clone_succeed = TRUE;
388 }
389
390 mpProgressScreen->DestroyWindow();
391 delete mpProgressScreen;
392 mpProgressScreen = NULL;
393
394 if (clone_succeed)
395 {
396 express_dialog::OnOK();
397 }
398 else
399 {
400 ShowHideChildren(SW_SHOW);
401 }
402 }
403
404 ///////////////////////////////////////////////////////////////////////////////
PopulateRecentProjects(CString clone_dir)405 void clone_repo_dialog::PopulateRecentProjects(CString clone_dir)
406 {
407 if (!clone_dir.IsEmpty())
408 {
409 clone_dir.MakeLower();
410 StudioXIni.recent_project_paths[0] = clone_dir + _T("\\samples\\demo_guix_washing_machine\\demo_guix_washing_machine.gxp");
411 StudioXIni.recent_project_paths[1] = clone_dir + _T("\\samples\\demo_guix_home_automation\\demo_guix_home_automation.gxp");
412 StudioXIni.recent_project_paths[2] = clone_dir + _T("\\samples\\demo_guix_car_infotainment\\demo_guix_car_infotainment.gxp");
413 StudioXIni.recent_project_paths[3] = clone_dir + _T("\\samples\\demo_guix_shapes\\guix_shapes.gxp");
414 StudioXIni.recent_project_paths[4] = clone_dir + _T("\\samples\\demo_guix_widget_types\\guix_widget_types.gxp");
415
416 // update recent projects menu
417 CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd();
418 pMain->UpdateRecentProjectsMenu();
419
420 target_view *target = GetTargetView();
421 if (target)
422 {
423 recent_project_win *dlg = (recent_project_win *) target->GetRecentDialog();
424 if (dlg)
425 {
426 dlg->UpdateRecentList();
427 }
428 }
429 }
430 }
431
432 ///////////////////////////////////////////////////////////////////////////////
OnSetDirectory()433 void clone_repo_dialog::OnSetDirectory()
434 {
435 TCHAR path[MAX_PATH];
436 char ascii_path[MAX_PATH];
437 CString test_project;
438 git_repository *repo;
439 size_t converted_chars;
440 BOOL ValidRepo = FALSE;
441
442 git_libgit2_init();
443
444 if (BrowseForFolder(this->GetSafeHwnd(), _T("Select local GUIX root directory.."), _T("C:\\"), path))
445 {
446 // Test to see if this is a git repository:
447
448 wcstombs_s(&converted_chars, ascii_path, (wcslen(path) + 1) * 2, path, MAX_PATH);
449
450 if (git_repository_open(&repo, ascii_path) != 0)
451 {
452 ErrorMsg("The selected directory does not appear to be a valid GUIX repository.", this);
453 }
454 else
455 {
456 git_repository_free(repo);
457
458 test_project = path;
459 test_project += "\\samples\\demo_guix_calculator\\guix_calculator.gxp";
460
461 StudioXIni.samples_dir = path;
462 WriteIniInfo();
463
464 if (FileExists(PATH_TYPE_ABSOLUTE, test_project))
465 {
466 PopulateRecentProjects(path);
467 ValidRepo = TRUE;
468 }
469 else
470 {
471 ErrorMsg("The selected directory doesn't contain the expected GUIX Studio sample projects. Please update your local GUIX repository.", this);
472 }
473 }
474 }
475 git_libgit2_shutdown();
476
477 if (ValidRepo)
478 {
479 CDialog::OnOK();
480 }
481
482 }
483
484 ///////////////////////////////////////////////////////////////////////////////
OnRepoCloneMsgUpdate(WPARAM wParam,LPARAM lParam)485 LRESULT clone_repo_dialog::OnRepoCloneMsgUpdate(WPARAM wParam, LPARAM lParam)
486 {
487 git_clone_info *info = (git_clone_info *)wParam;
488
489 if (info->stats_updated)
490 {
491 mpProgressScreen->UpdateIndexerProgress(&info->stats);
492 }
493
494 if (info->checkout_updated)
495 {
496 mpProgressScreen->UpdateCheckoutProgress(info->checkout_completed, info->checkout_total);
497 }
498
499 if (info->msg_updated)
500 {
501 if (info->msg_type == MSG_TYPE_CLONE)
502 {
503 mpProgressScreen->UpdateGitMessage(info->msg);
504 }
505 else if (info->msg_type == MSG_TYPE_COMPLETE)
506 {
507 CString msg("");
508
509 if (strnlen(info->msg, sizeof(info->msg)))
510 {
511 msg = _T("Unable to clone the GUIX repository to your local directory. The git reported error is: ");
512 msg += info->msg;
513 }
514 CompleteGUIXRepoClone(msg);
515 }
516 }
517
518 return 0;
519 }
520
521 ///////////////////////////////////////////////////////////////////////////////
ShowHideChildren(BOOL show)522 void clone_repo_dialog::ShowHideChildren(BOOL show)
523 {
524 GetDlgItem(IDC_CLONE_PROMPT)->ShowWindow(show);
525 GetDlgItem(IDB_CLONE_REPO)->ShowWindow(show);
526 GetDlgItem(IDB_SELECT_DIRECTORY)->ShowWindow(show);
527 GetDlgItem(IDB_EXIT_CLONE_DIALOG)->ShowWindow(show);
528 }
529
BEGIN_MESSAGE_MAP(git_progress_screen,CDialog)530 BEGIN_MESSAGE_MAP(git_progress_screen, CDialog)
531 ON_WM_CREATE()
532 END_MESSAGE_MAP()
533
534 ///////////////////////////////////////////////////////////////////////////////
535 git_progress_screen::git_progress_screen(CWnd *parent, CString clone_message):
536 CDialog(git_progress_screen::IDD, parent)
537 {
538 mCloneMessage = clone_message;
539 }
540
541
542 ///////////////////////////////////////////////////////////////////////////////
OnInitDialog()543 BOOL git_progress_screen::OnInitDialog()
544 {
545 CDialog::OnInitDialog();
546 CenterWindow();
547
548 CStatic *CloneMessage = (CStatic *) GetDlgItem(IDC_CLONE_OPERATION);
549 if (CloneMessage)
550 {
551 CloneMessage->SetWindowText(mCloneMessage);
552 }
553
554 CStatic* prog_field = (CStatic*)GetDlgItem(IDC_GIT_MESSAGE);
555 if (prog_field)
556 {
557 prog_field->SetWindowText(NULL);
558 }
559
560 prog_field = (CStatic *) GetDlgItem(IDC_OBJECT_COUNT);
561 if (prog_field)
562 {
563 prog_field->SetWindowText(NULL);
564 }
565 prog_field = (CStatic *) GetDlgItem(IDC_GIT_BYTES);
566 if (prog_field)
567 {
568 prog_field->SetWindowText(NULL);
569 }
570 prog_field = (CStatic *) GetDlgItem(IDC_GIT_ERROR);
571 if (prog_field)
572 {
573 prog_field->SetWindowText(NULL);
574 }
575 return TRUE;
576 }
577
578 ///////////////////////////////////////////////////////////////////////////////
UpdateGitMessage(const char * str)579 void git_progress_screen::UpdateGitMessage(const char *str)
580 {
581 CStatic *git_message = (CStatic *) GetDlgItem(IDC_GIT_MESSAGE);
582 if (git_message)
583 {
584 CString msg(str);
585 git_message->SetWindowText(msg);
586 }
587 }
588
589 ///////////////////////////////////////////////////////////////////////////////
UpdateIndexerProgress(const git_indexer_progress * stats)590 void git_progress_screen::UpdateIndexerProgress(const git_indexer_progress *stats)
591 {
592 CString stat_message;
593 CStatic *stat_field;
594
595 stat_field = (CStatic *) GetDlgItem(IDC_OBJECT_COUNT);
596
597 if (stat_field)
598 {
599 if (stats->received_objects != stats->total_objects)
600 {
601 if (stats->received_objects)
602 {
603 stat_message.Format(_T("Received %d of %d objects"), stats->received_objects, stats->total_objects);
604 }
605 else
606 {
607 stat_message.Format(_T("Indexed %d of %d objects"), stats->indexed_objects, stats->total_objects);
608 }
609 }
610 else
611 {
612 stat_message.Format(_T("Indexed %d of %d deltas"), stats->indexed_deltas, stats->total_deltas);
613 }
614 stat_field->SetWindowText(stat_message);
615 }
616
617 stat_field = (CStatic *) GetDlgItem(IDC_GIT_BYTES);
618 if (stat_field)
619 {
620 stat_message.Format(_T("%d Total Bytes Received"), stats->received_bytes);
621 stat_field->SetWindowTextW(stat_message);
622 }
623 }
624
625 ///////////////////////////////////////////////////////////////////////////////
UpdateCheckoutProgress(size_t completed,size_t total)626 void git_progress_screen::UpdateCheckoutProgress(size_t completed, size_t total)
627 {
628 CString stat_message;
629 CStatic *stat_field;
630
631 stat_field = (CStatic *)GetDlgItem(IDC_GIT_ERROR);
632 if (stat_field)
633 {
634 stat_message.Format(_T("Checking out %d of %d files"), completed, total);
635 stat_field->SetWindowTextW(stat_message);
636 }
637 }