KiCad PCB EDA Suite
Loading...
Searching...
No Matches
command_mergetool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include "command_mergetool.h"
25
26#include <cli/exit_codes.h>
28#include <kiface_base.h>
29#include <kiway.h>
30#include <paths.h>
31#include <string_utils.h>
32
33#include <wx/crt.h>
34#include <wx/filename.h>
35#include <wx/utils.h>
36
37
38#define ARG_ANCESTOR "ancestor"
39#define ARG_OURS "ours"
40#define ARG_THEIRS "theirs"
41#define ARG_OUTPUT_F "--output"
42
43
47static int translateExitCode( int aKicadExit )
48{
49 using namespace CLI::EXIT_CODES;
50
51 switch( aKicadExit )
52 {
53 case SUCCESS: return 0;
54 case ERR_CANCELLED: return 1;
55 case ERR_RC_VIOLATIONS: return 2; // unresolved conflicts
57 case ERR_INVALID_OUTPUT_CONFLICT: return 3; // I/O or parse error
58 default: return aKicadExit;
59 }
60}
61
62
64{
65 switch( aKind )
66 {
71 return true;
72
73 default:
74 return false;
75 }
76}
77
78
80 COMMAND( "mergetool" )
81{
82 m_argParser.add_description(
83 UTF8STDSTR( _( "Three-way merge driver for `git mergetool`. Detects PCB vs "
84 "schematic vs library item from the output extension, opens "
85 "the resolution dialog in the GUI kicad binary for any "
86 "unresolved conflicts where available, "
87 "and writes the merged file." ) ) );
88
89 m_argParser.add_argument( ARG_ANCESTOR )
90 .help( UTF8STDSTR( _( "Common-ancestor file (.kicad_pcb, .kicad_sch, "
91 ".kicad_sym, or .kicad_mod)" ) ) )
92 .metavar( "ANCESTOR" );
93
94 m_argParser.add_argument( ARG_OURS )
95 .help( UTF8STDSTR( _( "'Ours' file" ) ) )
96 .metavar( "OURS" );
97
98 m_argParser.add_argument( ARG_THEIRS )
99 .help( UTF8STDSTR( _( "'Theirs' file" ) ) )
100 .metavar( "THEIRS" );
101
102 m_argParser.add_argument( "-o", ARG_OUTPUT_F )
103 .required()
104 .help( UTF8STDSTR( _( "Output path; extension picks the document type" ) ) )
105 .metavar( "PATH" );
106}
107
108
110{
111 const wxString ancestor = From_UTF8( m_argParser.get<std::string>( ARG_ANCESTOR ).c_str() );
112 const wxString ours = From_UTF8( m_argParser.get<std::string>( ARG_OURS ).c_str() );
113 const wxString theirs = From_UTF8( m_argParser.get<std::string>( ARG_THEIRS ).c_str() );
114 const wxString output = From_UTF8( m_argParser.get<std::string>( ARG_OUTPUT_F ).c_str() );
115
116 if( output.IsEmpty() )
117 {
118 wxFprintf( stderr, _( "--output is required\n" ) );
120 }
121
123
124 if( !supportsMergetoolKind( kind ) )
125 {
126 wxFprintf( stderr,
127 _( "Unsupported output extension '%s'. "
128 "Expected .kicad_pcb, .kicad_sch, .kicad_sym, or .kicad_mod\n" ),
129 output );
130 return EXIT_CODES::ERR_INVALID_INPUT_FILE; // contract code 3 — bad input
131 }
132
133 // The conflict-resolution dialog needs a GUI, which kicad-cli doesn't
134 // provide. Re-exec the main `kicad` binary in --mergetool mode so the
135 // user gets the interactive dialog. This is the documented Phase 8
136 // contract for `git mergetool`.
137 const wxString kicadBinPath = PATHS::ResolveSiblingExecutable( wxT( "kicad" ) );
138
139 if( kicadBinPath.IsEmpty() )
140 {
141 wxFprintf( stderr,
142 _( "Cannot find the main kicad binary alongside kicad-cli.\n" ) );
143 return 4; // contract code 4 — initialization failure
144 }
145
146 // Pass argv as an array so paths containing spaces or quotes aren't
147 // mis-parsed by the string-form wxExecute's shell-style tokenizer.
148 // wxExecute's array form needs c_str()-backed pointers, so we hold
149 // wxCharBuffer / wxScopedCharBuffer locals to keep them alive.
150 const wxScopedCharBuffer args[] = {
151 kicadBinPath.utf8_str(),
152 wxScopedCharBuffer::CreateNonOwned( "--mergetool" ),
153 ancestor.utf8_str(),
154 ours.utf8_str(),
155 theirs.utf8_str(),
156 output.utf8_str()
157 };
158
159 const char* argv[] = { args[0].data(), args[1].data(), args[2].data(),
160 args[3].data(), args[4].data(), args[5].data(),
161 nullptr };
162
163 const long rc = wxExecute( const_cast<char**>( argv ), wxEXEC_SYNC );
164
165 if( rc < 0 )
166 {
167 wxFprintf( stderr, _( "Failed to launch '%s'\n" ), kicadBinPath );
168 return 4;
169 }
170
171 return translateExitCode( static_cast<int>( rc ) );
172}
argparse::ArgumentParser m_argParser
Definition command.h:113
COMMAND(const std::string &aName)
Define a new COMMAND instance.
Definition command.cpp:30
int doPerform(KIWAY &aKiway) override
The internal handler that should be overloaded to implement command specific processing and work.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:348
static wxString ResolveSiblingExecutable(const wxString &aBaseName)
Resolve a sibling executable alongside the running process.
Definition paths.cpp:669
#define UTF8STDSTR(s)
Definition command.h:27
#define ARG_ANCESTOR
#define ARG_THEIRS
#define ARG_OUTPUT_F
#define ARG_OURS
static bool supportsMergetoolKind(KICAD_DIFF::DOC_KIND aKind)
static int translateExitCode(int aKicadExit)
Map kicad-side exit codes to the Phase 8 mergetool contract documented in the header.
#define _(s)
static wxString From_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition idf_helpers.h:44
static const int ERR_ARGS
Definition exit_codes.h:31
static const int ERR_RC_VIOLATIONS
Rules check violation count was greater than 0.
Definition exit_codes.h:37
static const int ERR_CANCELLED
The user cancelled the operation.
Definition exit_codes.h:45
static const int ERR_INVALID_INPUT_FILE
Definition exit_codes.h:33
static const int ERR_INVALID_OUTPUT_CONFLICT
Definition exit_codes.h:34
DOC_KIND DocKindFromExtension(const wxString &aPath)
Map a path's extension to a DOC_KIND (.kicad_pcb -> PCB, .kicad_sch -> SCH, .kicad_sym -> SYM_LIB,...
DOC_KIND
Document type a diff/merge entry point should route to, derived from a file path's extension.