1
0
mirror of https://github.com/azure-rtos/guix.git synced 2025-01-28 07:03:11 +08:00
guix/guix_studio/clone_repo_dialog.cpp
2024-02-21 17:53:49 +08:00

637 lines
18 KiB
C++

// clone_repo_dialog.cpp : implementation file
//
#include "studiox_includes.h"
#include "clone_repo_dialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
char guix_repo_url[] = "https://github.com/eclipse-threadx/guix.git";
BEGIN_MESSAGE_MAP(clone_repo_dialog, express_dialog)
ON_WM_CLOSE()
ON_BN_CLICKED(IDB_EXIT_CLONE_DIALOG, OnCancel)
ON_BN_CLICKED(IDB_CLONE_REPO, OnCloneRepo)
ON_BN_CLICKED(IDB_SELECT_DIRECTORY, OnSetDirectory)
ON_MESSAGE(USR_MSG_REPO_CLONE_MSG_UPDATE, OnRepoCloneMsgUpdate)
END_MESSAGE_MAP()
IMPLEMENT_DYNAMIC(clone_repo_dialog, express_dialog)
#define GIT_CLONE_MSG_UPDATE_PERIOD 100 /* ms */
#define MSG_TYPE_CLONE 0
#define MSG_TYPE_COMPLETE 1
struct git_clone_info {
char msg[MAX_PATH];
int msg_type;
BOOL msg_updated;
git_indexer_progress stats;
BOOL stats_updated;
int checkout_completed;
int checkout_total;
BOOL checkout_updated;
};
extern INI_INFO StudioXIni;
extern CString target_class_name;
static git_clone_info CloneInfo;
///////////////////////////////////////////////////////////////////////////////
static BOOL WriteCloneInfoToPipe(HANDLE writePipe, git_clone_info *info)
{
if (writePipe)
{
DWORD writtenSize;
BOOL result;
result = WriteFile(writePipe, info, sizeof(git_clone_info), &writtenSize, NULL);
if (writtenSize == sizeof(git_clone_info) && result)
{
info->checkout_updated = FALSE;
info->msg_updated = FALSE;
info->stats_updated = FALSE;
return TRUE;
}
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
static BOOL ReadCloneInfoFromPipe(HANDLE readPipe, git_clone_info *info)
{
DWORD readSize;
if (readPipe)
{
BOOL result;
result = ReadFile(readPipe, info, sizeof(git_clone_info), &readSize, NULL);
if (readSize == sizeof(git_clone_info) && result)
{
return TRUE;
}
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
clone_repo_dialog::clone_repo_dialog(CWnd *parent)
: express_dialog(clone_repo_dialog::IDD, parent)
{
IconId = IDB_WARNING;
SetTitleText(CString("Clone GUIX Repository"));
ZeroMemory(&mProcessInfo, sizeof(PROCESS_INFORMATION));
mpProgressScreen = NULL;
mWritePipeHandle = 0;
mReadPipeHandle = 0;
}
///////////////////////////////////////////////////////////////////////////////
BOOL clone_repo_dialog::OnInitDialog()
{
express_dialog::OnInitDialog();
AddCancelButton();
return TRUE; // return TRUE unless you set the focus to a control
}
///////////////////////////////////////////////////////////////////////////////
void clone_repo_dialog::OnClose()
{
CDialog::OnClose();
}
///////////////////////////////////////////////////////////////////////////////
void clone_repo_dialog::OnCancel()
{
if (mpProgressScreen)
{
CompleteGUIXRepoClone(L"GUIX repository clone aborted!");
}
else
{
express_dialog::OnCancel();
}
}
///////////////////////////////////////////////////////////////////////////////
int guix_transport_message_cb(const char *str, int len, void *payload)
{
if (len >= MAX_PATH)
{
len = MAX_PATH - 1;
}
memcpy_s(CloneInfo.msg, MAX_PATH, str, len);
CloneInfo.msg[len] = 0;
CloneInfo.msg_type = MSG_TYPE_CLONE;
CloneInfo.msg_updated = TRUE;
return 0;
}
///////////////////////////////////////////////////////////////////////////////
int guix_indexer_progress_cb(const git_indexer_progress *stats, void *payload)
{
CloneInfo.stats = *stats;
CloneInfo.stats_updated = TRUE;
return 0;
}
///////////////////////////////////////////////////////////////////////////////
void guix_checkout_progress_cb(const char *path, size_t completed_steps, size_t total_steps, void *payload)
{
CloneInfo.checkout_completed = completed_steps;
CloneInfo.checkout_total = total_steps;
CloneInfo.checkout_updated = TRUE;
}
///////////////////////////////////////////////////////////////////////////////
void CALLBACK OnWriteCloneInfoTimer(void* lpParametar, BOOLEAN TimerOrWaitFired)
{
HANDLE write_pipe_handle = (HANDLE)lpParametar;
if (CloneInfo.msg_updated ||
CloneInfo.checkout_updated ||
CloneInfo.stats_updated)
{
WriteCloneInfoToPipe(write_pipe_handle, &CloneInfo);
}
}
///////////////////////////////////////////////////////////////////////////////
void CloneGUIXRepo(char *local_path, HANDLE write_pipe_handle)
{
BOOL clone_success = FALSE;
git_repository* clone_out;
git_clone_options options;
git_remote_callbacks remote_callbacks;
git_checkout_options checkout_opts;
git_fetch_options fetch_options = GIT_FETCH_OPTIONS_INIT;
git_libgit2_init();
git_clone_options_init(&options, GIT_CLONE_OPTIONS_VERSION);
remote_callbacks = fetch_options.callbacks;
remote_callbacks.sideband_progress = guix_transport_message_cb;
remote_callbacks.transfer_progress = guix_indexer_progress_cb;
fetch_options.callbacks = remote_callbacks;
options.fetch_opts = fetch_options;
checkout_opts = options.checkout_opts;
checkout_opts.progress_cb = guix_checkout_progress_cb;
options.checkout_opts = checkout_opts;
CloneInfo.checkout_updated = FALSE;
CloneInfo.msg_updated = FALSE;
CloneInfo.stats_updated = FALSE;
// Create a timer to write git clone information to named pipe memory.
HANDLE hTimer;
BOOL success = ::CreateTimerQueueTimer(
&hTimer,
NULL,
OnWriteCloneInfoTimer,
(PVOID)write_pipe_handle,
GIT_CLONE_MSG_UPDATE_PERIOD,
GIT_CLONE_MSG_UPDATE_PERIOD,
WT_EXECUTEINTIMERTHREAD);
int clone_status = git_clone(&clone_out, guix_repo_url, local_path, &options);
// Delete timer
DeleteTimerQueueTimer(NULL, hTimer, NULL);
const git_error* error = NULL;
if (clone_status)
{
error = git_error_last();
}
else
{
git_repository_free(clone_out);
}
if(error)
{
memcpy_s(CloneInfo.msg, MAX_PATH, error->message, strnlen(error->message, MAX_PATH - 1) + 1);
}
else
{
CloneInfo.msg[0] = '\0';
}
git_libgit2_shutdown();
// Nofity the calling process that git clone is completed.
CloneInfo.msg_type = MSG_TYPE_COMPLETE;
CloneInfo.msg_updated = TRUE;
WriteCloneInfoToPipe(write_pipe_handle , &CloneInfo);
}
///////////////////////////////////////////////////////////////////////////////
static DWORD WINAPI ReadCloneInfoThreadEntry(LPVOID thread_input)
{
clone_repo_dialog *parent = (clone_repo_dialog *)thread_input;
HANDLE read_pipe_handle = parent->GetReadPipeHandle();
git_clone_info info;
while (1)
{
if (ReadCloneInfoFromPipe(read_pipe_handle, &info))
{
parent->SendMessage(USR_MSG_REPO_CLONE_MSG_UPDATE, (WPARAM)&info, NULL);
}
}
}
///////////////////////////////////////////////////////////////////////////////
void clone_repo_dialog::OnCloneRepo()
{
TCHAR folder_path[MAX_PATH];
char *repo_url = guix_repo_url;
if (BrowseForFolder(this->GetSafeHwnd(), _T("Select root for GUIX git repository clone..."), _T("C:\\Eclipse_ThreadX"), folder_path))
{
//"git clone --depth 1 aka.ms/azrtos-guix-repo --branch master --single-branch"
CString CloneMessage(_T("Cloning from source URL "));
CloneMessage += repo_url;
CloneMessage += " to\n";
CloneMessage += "local folder ";
CloneMessage += folder_path;
CloneMessage += "\\guix";
mpProgressScreen = new git_progress_screen(this, CloneMessage);
mpProgressScreen->Create(IDD_GIT_PROGRESS, this);
mpProgressScreen->ShowWindow(SW_SHOW);
ShowHideChildren(SW_HIDE);
mLocalPath.Format(L"%s\\guix", folder_path);
// Run a subprocess to execute guix repo clone
TCHAR exename[MAX_PATH];
GetModuleFileName(NULL, exename, MAX_PATH);
SECURITY_ATTRIBUTES security_attri;
security_attri.nLength = sizeof(SECURITY_ATTRIBUTES);
security_attri.lpSecurityDescriptor = NULL;
security_attri.bInheritHandle = TRUE;
if(!CreatePipe(&mReadPipeHandle, &mWritePipeHandle, &security_attri, 0))
{
ErrorMsg("Create git clone named pipe memory failed!", this);
return;
}
// Start a work thread to read git clone message from pipe.
mReadCloneInfoThreadHandle = CreateThread(NULL, GX_WIN32_STACK_SIZE, (LPTHREAD_START_ROUTINE)ReadCloneInfoThreadEntry, (LPVOID)this, 0, 0);
if (mReadCloneInfoThreadHandle == INVALID_HANDLE_VALUE)
{
ErrorMsg("Create thread failed!", this);
mReadCloneInfoThreadHandle = 0;
return;
}
CString szCmdLine;
szCmdLine.Format(L"%s -n --clone_guix_repo \"%s\" --write_pipe_handle %d", exename, mLocalPath, mWritePipeHandle);
STARTUPINFO siStartInfo;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&mProcessInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
// Create the child process.
if(!CreateProcess(NULL,
szCmdLine.GetBuffer(), // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&mProcessInfo // receives PROCESS_INFORMATION
))
{
ErrorMsg("Create git clone process failed!", this);
return;
}
}
}
///////////////////////////////////////////////////////////////////////////////
void clone_repo_dialog::CompleteGUIXRepoClone(CString msg)
{
// Terminate git clone process
TerminateProcess(mProcessInfo.hProcess, 0);
// Close handles to the child process and its primary thread.
CloseHandle(mProcessInfo.hProcess);
CloseHandle(mProcessInfo.hThread);
ZeroMemory(&mProcessInfo, sizeof(PROCESS_INFORMATION));
// Close read and write pipe handles.
if (mReadPipeHandle)
{
CloseHandle(mReadPipeHandle);
mReadPipeHandle = 0;
}
if (mWritePipeHandle)
{
CloseHandle(mWritePipeHandle);
mWritePipeHandle = 0;
}
// Terminate clone information read thread.
if (mReadCloneInfoThreadHandle)
{
TerminateThread(mReadCloneInfoThreadHandle, 0);
CloseHandle(mReadCloneInfoThreadHandle);
mReadCloneInfoThreadHandle = 0;
}
BOOL clone_succeed = FALSE;
if (!msg.IsEmpty())
{
ErrorMsg(CW2A(msg), this);
}
else
{
StudioXIni.samples_dir = mLocalPath;
WriteIniInfo();
clone_repo_dialog::PopulateRecentProjects(StudioXIni.samples_dir);
Notify("The GUIX sample projects are now installed.", this);
clone_succeed = TRUE;
}
mpProgressScreen->DestroyWindow();
delete mpProgressScreen;
mpProgressScreen = NULL;
if (clone_succeed)
{
express_dialog::OnOK();
}
else
{
ShowHideChildren(SW_SHOW);
}
}
///////////////////////////////////////////////////////////////////////////////
void clone_repo_dialog::PopulateRecentProjects(CString clone_dir)
{
if (!clone_dir.IsEmpty())
{
clone_dir.MakeLower();
StudioXIni.recent_project_paths[0] = clone_dir + _T("\\samples\\demo_guix_washing_machine\\demo_guix_washing_machine.gxp");
StudioXIni.recent_project_paths[1] = clone_dir + _T("\\samples\\demo_guix_home_automation\\demo_guix_home_automation.gxp");
StudioXIni.recent_project_paths[2] = clone_dir + _T("\\samples\\demo_guix_car_infotainment\\demo_guix_car_infotainment.gxp");
StudioXIni.recent_project_paths[3] = clone_dir + _T("\\samples\\demo_guix_shapes\\guix_shapes.gxp");
StudioXIni.recent_project_paths[4] = clone_dir + _T("\\samples\\demo_guix_widget_types\\guix_widget_types.gxp");
// update recent projects menu
CMainFrame* pMain = (CMainFrame*)AfxGetMainWnd();
pMain->UpdateRecentProjectsMenu();
target_view *target = GetTargetView();
if (target)
{
recent_project_win *dlg = (recent_project_win *) target->GetRecentDialog();
if (dlg)
{
dlg->UpdateRecentList();
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
void clone_repo_dialog::OnSetDirectory()
{
TCHAR path[MAX_PATH];
char ascii_path[MAX_PATH];
CString test_project;
git_repository *repo;
size_t converted_chars;
BOOL ValidRepo = FALSE;
git_libgit2_init();
if (BrowseForFolder(this->GetSafeHwnd(), _T("Select local GUIX root directory.."), _T("C:\\"), path))
{
// Test to see if this is a git repository:
wcstombs_s(&converted_chars, ascii_path, (wcslen(path) + 1) * 2, path, MAX_PATH);
if (git_repository_open(&repo, ascii_path) != 0)
{
ErrorMsg("The selected directory does not appear to be a valid GUIX repository.", this);
}
else
{
git_repository_free(repo);
test_project = path;
test_project += "\\samples\\demo_guix_calculator\\guix_calculator.gxp";
StudioXIni.samples_dir = path;
WriteIniInfo();
if (FileExists(PATH_TYPE_ABSOLUTE, test_project))
{
PopulateRecentProjects(path);
ValidRepo = TRUE;
}
else
{
ErrorMsg("The selected directory doesn't contain the expected GUIX Studio sample projects. Please update your local GUIX repository.", this);
}
}
}
git_libgit2_shutdown();
if (ValidRepo)
{
CDialog::OnOK();
}
}
///////////////////////////////////////////////////////////////////////////////
LRESULT clone_repo_dialog::OnRepoCloneMsgUpdate(WPARAM wParam, LPARAM lParam)
{
git_clone_info *info = (git_clone_info *)wParam;
if (info->stats_updated)
{
mpProgressScreen->UpdateIndexerProgress(&info->stats);
}
if (info->checkout_updated)
{
mpProgressScreen->UpdateCheckoutProgress(info->checkout_completed, info->checkout_total);
}
if (info->msg_updated)
{
if (info->msg_type == MSG_TYPE_CLONE)
{
mpProgressScreen->UpdateGitMessage(info->msg);
}
else if (info->msg_type == MSG_TYPE_COMPLETE)
{
CString msg("");
if (strnlen(info->msg, sizeof(info->msg)))
{
msg = _T("Unable to clone the GUIX repository to your local directory. The git reported error is: ");
msg += info->msg;
}
CompleteGUIXRepoClone(msg);
}
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
void clone_repo_dialog::ShowHideChildren(BOOL show)
{
GetDlgItem(IDC_CLONE_PROMPT)->ShowWindow(show);
GetDlgItem(IDB_CLONE_REPO)->ShowWindow(show);
GetDlgItem(IDB_SELECT_DIRECTORY)->ShowWindow(show);
GetDlgItem(IDB_EXIT_CLONE_DIALOG)->ShowWindow(show);
}
BEGIN_MESSAGE_MAP(git_progress_screen, CDialog)
ON_WM_CREATE()
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////////
git_progress_screen::git_progress_screen(CWnd *parent, CString clone_message):
CDialog(git_progress_screen::IDD, parent)
{
mCloneMessage = clone_message;
}
///////////////////////////////////////////////////////////////////////////////
BOOL git_progress_screen::OnInitDialog()
{
CDialog::OnInitDialog();
CenterWindow();
CStatic *CloneMessage = (CStatic *) GetDlgItem(IDC_CLONE_OPERATION);
if (CloneMessage)
{
CloneMessage->SetWindowText(mCloneMessage);
}
CStatic* prog_field = (CStatic*)GetDlgItem(IDC_GIT_MESSAGE);
if (prog_field)
{
prog_field->SetWindowText(NULL);
}
prog_field = (CStatic *) GetDlgItem(IDC_OBJECT_COUNT);
if (prog_field)
{
prog_field->SetWindowText(NULL);
}
prog_field = (CStatic *) GetDlgItem(IDC_GIT_BYTES);
if (prog_field)
{
prog_field->SetWindowText(NULL);
}
prog_field = (CStatic *) GetDlgItem(IDC_GIT_ERROR);
if (prog_field)
{
prog_field->SetWindowText(NULL);
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
void git_progress_screen::UpdateGitMessage(const char *str)
{
CStatic *git_message = (CStatic *) GetDlgItem(IDC_GIT_MESSAGE);
if (git_message)
{
CString msg(str);
git_message->SetWindowText(msg);
}
}
///////////////////////////////////////////////////////////////////////////////
void git_progress_screen::UpdateIndexerProgress(const git_indexer_progress *stats)
{
CString stat_message;
CStatic *stat_field;
stat_field = (CStatic *) GetDlgItem(IDC_OBJECT_COUNT);
if (stat_field)
{
if (stats->received_objects != stats->total_objects)
{
if (stats->received_objects)
{
stat_message.Format(_T("Received %d of %d objects"), stats->received_objects, stats->total_objects);
}
else
{
stat_message.Format(_T("Indexed %d of %d objects"), stats->indexed_objects, stats->total_objects);
}
}
else
{
stat_message.Format(_T("Indexed %d of %d deltas"), stats->indexed_deltas, stats->total_deltas);
}
stat_field->SetWindowText(stat_message);
}
stat_field = (CStatic *) GetDlgItem(IDC_GIT_BYTES);
if (stat_field)
{
stat_message.Format(_T("%d Total Bytes Received"), stats->received_bytes);
stat_field->SetWindowTextW(stat_message);
}
}
///////////////////////////////////////////////////////////////////////////////
void git_progress_screen::UpdateCheckoutProgress(size_t completed, size_t total)
{
CString stat_message;
CStatic *stat_field;
stat_field = (CStatic *)GetDlgItem(IDC_GIT_ERROR);
if (stat_field)
{
stat_message.Format(_T("Checking out %d of %d files"), completed, total);
stat_field->SetWindowTextW(stat_message);
}
}